Update jemalloc to version 5.2.1
Revert r354606 to restore r354605. Apply one line from jemalloc commit d01b425e5d1e1 in hash_x86_128() to fix the build with gcc, which only allows a fallthrough attribute to appear before a case or default label. Submitted by: jasone in r354605 Discussed with: jasone Reviewed by: bdrewery MFC after: never, due to gcc 4.2.1 Relnotes: yes Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D24522
This commit is contained in:
parent
712b699779
commit
9cea565ffa
@ -1,10 +1,10 @@
|
||||
Unless otherwise specified, files in the jemalloc source distribution are
|
||||
subject to the following license:
|
||||
--------------------------------------------------------------------------------
|
||||
Copyright (C) 2002-2018 Jason Evans <jasone@canonware.com>.
|
||||
Copyright (C) 2002-present Jason Evans <jasone@canonware.com>.
|
||||
All rights reserved.
|
||||
Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
Copyright (C) 2009-2018 Facebook, Inc. All rights reserved.
|
||||
Copyright (C) 2009-present 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:
|
||||
|
@ -4,7 +4,143 @@ brevity. Much more detail can be found in the git revision history:
|
||||
|
||||
https://github.com/jemalloc/jemalloc
|
||||
|
||||
* 5.1.0 (May 4th, 2018)
|
||||
* 5.2.1 (August 5, 2019)
|
||||
|
||||
This release is primarily about Windows. A critical virtual memory leak is
|
||||
resolved on all Windows platforms. The regression was present in all releases
|
||||
since 5.0.0.
|
||||
|
||||
Bug fixes:
|
||||
- Fix a severe virtual memory leak on Windows. This regression was first
|
||||
released in 5.0.0. (@Ignition, @j0t, @frederik-h, @davidtgoldblatt,
|
||||
@interwq)
|
||||
- Fix size 0 handling in posix_memalign(). This regression was first released
|
||||
in 5.2.0. (@interwq)
|
||||
- Fix the prof_log unit test which may observe unexpected backtraces from
|
||||
compiler optimizations. The test was first added in 5.2.0. (@marxin,
|
||||
@gnzlbg, @interwq)
|
||||
- Fix the declaration of the extent_avail tree. This regression was first
|
||||
released in 5.1.0. (@zoulasc)
|
||||
- Fix an incorrect reference in jeprof. This functionality was first released
|
||||
in 3.0.0. (@prehistoric-penguin)
|
||||
- Fix an assertion on the deallocation fast-path. This regression was first
|
||||
released in 5.2.0. (@yinan1048576)
|
||||
- Fix the TLS_MODEL attribute in headers. This regression was first released
|
||||
in 5.0.0. (@zoulasc, @interwq)
|
||||
|
||||
Optimizations and refactors:
|
||||
- Implement opt.retain on Windows and enable by default on 64-bit. (@interwq,
|
||||
@davidtgoldblatt)
|
||||
- Optimize away a branch on the operator delete[] path. (@mgrice)
|
||||
- Add format annotation to the format generator function. (@zoulasc)
|
||||
- Refactor and improve the size class header generation. (@yinan1048576)
|
||||
- Remove best fit. (@djwatson)
|
||||
- Avoid blocking on background thread locks for stats. (@oranagra, @interwq)
|
||||
|
||||
* 5.2.0 (April 2, 2019)
|
||||
|
||||
This release includes a few notable improvements, which are summarized below:
|
||||
1) improved fast-path performance from the optimizations by @djwatson; 2)
|
||||
reduced virtual memory fragmentation and metadata usage; and 3) bug fixes on
|
||||
setting the number of background threads. In addition, peak / spike memory
|
||||
usage is improved with certain allocation patterns. As usual, the release and
|
||||
prior dev versions have gone through large-scale production testing.
|
||||
|
||||
New features:
|
||||
- Implement oversize_threshold, which uses a dedicated arena for allocations
|
||||
crossing the specified threshold to reduce fragmentation. (@interwq)
|
||||
- Add extents usage information to stats. (@tyleretzel)
|
||||
- Log time information for sampled allocations. (@tyleretzel)
|
||||
- Support 0 size in sdallocx. (@djwatson)
|
||||
- Output rate for certain counters in malloc_stats. (@zinoale)
|
||||
- Add configure option --enable-readlinkat, which allows the use of readlinkat
|
||||
over readlink. (@davidtgoldblatt)
|
||||
- Add configure options --{enable,disable}-{static,shared} to allow not
|
||||
building unwanted libraries. (@Ericson2314)
|
||||
- Add configure option --disable-libdl to enable fully static builds.
|
||||
(@interwq)
|
||||
- Add mallctl interfaces:
|
||||
+ opt.oversize_threshold (@interwq)
|
||||
+ stats.arenas.<i>.extent_avail (@tyleretzel)
|
||||
+ stats.arenas.<i>.extents.<j>.n{dirty,muzzy,retained} (@tyleretzel)
|
||||
+ stats.arenas.<i>.extents.<j>.{dirty,muzzy,retained}_bytes
|
||||
(@tyleretzel)
|
||||
|
||||
Portability improvements:
|
||||
- Update MSVC builds. (@maksqwe, @rustyx)
|
||||
- Workaround a compiler optimizer bug on s390x. (@rkmisra)
|
||||
- Make use of pthread_set_name_np(3) on FreeBSD. (@trasz)
|
||||
- Implement malloc_getcpu() to enable percpu_arena for windows. (@santagada)
|
||||
- Link against -pthread instead of -lpthread. (@paravoid)
|
||||
- Make background_thread not dependent on libdl. (@interwq)
|
||||
- Add stringify to fix a linker directive issue on MSVC. (@daverigby)
|
||||
- Detect and fall back when 8-bit atomics are unavailable. (@interwq)
|
||||
- Fall back to the default pthread_create if dlsym(3) fails. (@interwq)
|
||||
|
||||
Optimizations and refactors:
|
||||
- Refactor the TSD module. (@davidtgoldblatt)
|
||||
- Avoid taking extents_muzzy mutex when muzzy is disabled. (@interwq)
|
||||
- Avoid taking large_mtx for auto arenas on the tcache flush path. (@interwq)
|
||||
- Optimize ixalloc by avoiding a size lookup. (@interwq)
|
||||
- Implement opt.oversize_threshold which uses a dedicated arena for requests
|
||||
crossing the threshold, also eagerly purges the oversize extents. Default
|
||||
the threshold to 8 MiB. (@interwq)
|
||||
- Clean compilation with -Wextra. (@gnzlbg, @jasone)
|
||||
- Refactor the size class module. (@davidtgoldblatt)
|
||||
- Refactor the stats emitter. (@tyleretzel)
|
||||
- Optimize pow2_ceil. (@rkmisra)
|
||||
- Avoid runtime detection of lazy purging on FreeBSD. (@trasz)
|
||||
- Optimize mmap(2) alignment handling on FreeBSD. (@trasz)
|
||||
- Improve error handling for THP state initialization. (@jsteemann)
|
||||
- Rework the malloc() fast path. (@djwatson)
|
||||
- Rework the free() fast path. (@djwatson)
|
||||
- Refactor and optimize the tcache fill / flush paths. (@djwatson)
|
||||
- Optimize sync / lwsync on PowerPC. (@chmeeedalf)
|
||||
- Bypass extent_dalloc() when retain is enabled. (@interwq)
|
||||
- Optimize the locking on large deallocation. (@interwq)
|
||||
- Reduce the number of pages committed from sanity checking in debug build.
|
||||
(@trasz, @interwq)
|
||||
- Deprecate OSSpinLock. (@interwq)
|
||||
- Lower the default number of background threads to 4 (when the feature
|
||||
is enabled). (@interwq)
|
||||
- Optimize the trylock spin wait. (@djwatson)
|
||||
- Use arena index for arena-matching checks. (@interwq)
|
||||
- Avoid forced decay on thread termination when using background threads.
|
||||
(@interwq)
|
||||
- Disable muzzy decay by default. (@djwatson, @interwq)
|
||||
- Only initialize libgcc unwinder when profiling is enabled. (@paravoid,
|
||||
@interwq)
|
||||
|
||||
Bug fixes (all only relevant to jemalloc 5.x):
|
||||
- Fix background thread index issues with max_background_threads. (@djwatson,
|
||||
@interwq)
|
||||
- Fix stats output for opt.lg_extent_max_active_fit. (@interwq)
|
||||
- Fix opt.prof_prefix initialization. (@davidtgoldblatt)
|
||||
- Properly trigger decay on tcache destroy. (@interwq, @amosbird)
|
||||
- Fix tcache.flush. (@interwq)
|
||||
- Detect whether explicit extent zero out is necessary with huge pages or
|
||||
custom extent hooks, which may change the purge semantics. (@interwq)
|
||||
- Fix a side effect caused by extent_max_active_fit combined with decay-based
|
||||
purging, where freed extents can accumulate and not be reused for an
|
||||
extended period of time. (@interwq, @mpghf)
|
||||
- Fix a missing unlock on extent register error handling. (@zoulasc)
|
||||
|
||||
Testing:
|
||||
- Simplify the Travis script output. (@gnzlbg)
|
||||
- Update the test scripts for FreeBSD. (@devnexen)
|
||||
- Add unit tests for the producer-consumer pattern. (@interwq)
|
||||
- Add Cirrus-CI config for FreeBSD builds. (@jasone)
|
||||
- Add size-matching sanity checks on tcache flush. (@davidtgoldblatt,
|
||||
@interwq)
|
||||
|
||||
Incompatible changes:
|
||||
- Remove --with-lg-page-sizes. (@davidtgoldblatt)
|
||||
|
||||
Documentation:
|
||||
- Attempt to build docs by default, however skip doc building when xsltproc
|
||||
is missing. (@interwq, @cmuellner)
|
||||
|
||||
* 5.1.0 (May 4, 2018)
|
||||
|
||||
This release is primarily about fine-tuning, ranging from several new features
|
||||
to numerous notable performance and portability enhancements. The release and
|
||||
|
@ -1,6 +1,7 @@
|
||||
$FreeBSD$
|
||||
.appveyor.yml
|
||||
.autom4te.cfg
|
||||
.cirrus.yml
|
||||
.git*
|
||||
.travis.yml
|
||||
FREEBSD-*
|
||||
|
@ -1,5 +1,5 @@
|
||||
diff --git a/doc/jemalloc.xml.in b/doc/jemalloc.xml.in
|
||||
index 1e12fd3a..c42a7e10 100644
|
||||
index 7fecda7c..d5ca5e86 100644
|
||||
--- a/doc/jemalloc.xml.in
|
||||
+++ b/doc/jemalloc.xml.in
|
||||
@@ -53,11 +53,22 @@
|
||||
@ -26,7 +26,7 @@ index 1e12fd3a..c42a7e10 100644
|
||||
<refsect2>
|
||||
<title>Standard API</title>
|
||||
<funcprototype>
|
||||
@@ -3376,4 +3387,18 @@ malloc_conf = "narenas:1";]]></programlisting></para>
|
||||
@@ -3510,4 +3521,18 @@ malloc_conf = "narenas:1";]]></programlisting></para>
|
||||
<para>The <function>posix_memalign()</function> function conforms
|
||||
to IEEE Std 1003.1-2001 (<quote>POSIX.1</quote>).</para>
|
||||
</refsect1>
|
||||
@ -45,26 +45,8 @@ index 1e12fd3a..c42a7e10 100644
|
||||
+ 11.0.</para>
|
||||
+ </refsect1>
|
||||
</refentry>
|
||||
diff --git a/include/jemalloc/internal/hooks.h b/include/jemalloc/internal/hooks.h
|
||||
index cd49afcb..85e2a991 100644
|
||||
--- a/include/jemalloc/internal/hooks.h
|
||||
+++ b/include/jemalloc/internal/hooks.h
|
||||
@@ -6,13 +6,6 @@ extern JEMALLOC_EXPORT void (*hooks_libc_hook)();
|
||||
|
||||
#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
|
||||
|
||||
-#define open JEMALLOC_HOOK(open, hooks_libc_hook)
|
||||
-#define read JEMALLOC_HOOK(read, hooks_libc_hook)
|
||||
-#define write JEMALLOC_HOOK(write, hooks_libc_hook)
|
||||
-#define readlink JEMALLOC_HOOK(readlink, hooks_libc_hook)
|
||||
-#define close JEMALLOC_HOOK(close, hooks_libc_hook)
|
||||
-#define creat JEMALLOC_HOOK(creat, hooks_libc_hook)
|
||||
-#define secure_getenv JEMALLOC_HOOK(secure_getenv, hooks_libc_hook)
|
||||
/* Note that this is undef'd and re-define'd in src/prof.c. */
|
||||
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)
|
||||
|
||||
diff --git a/include/jemalloc/internal/jemalloc_internal_decls.h b/include/jemalloc/internal/jemalloc_internal_decls.h
|
||||
index be70df51..84cd70da 100644
|
||||
index 7d6053e2..a0e4f5af 100644
|
||||
--- a/include/jemalloc/internal/jemalloc_internal_decls.h
|
||||
+++ b/include/jemalloc/internal/jemalloc_internal_decls.h
|
||||
@@ -1,6 +1,9 @@
|
||||
@ -77,8 +59,23 @@ index be70df51..84cd70da 100644
|
||||
#include <math.h>
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
diff --git a/include/jemalloc/internal/jemalloc_internal_defs_FreeBSD.h b/include/jemalloc/internal/jemalloc_internal_defs_FreeBSD.h
|
||||
new file mode 100644
|
||||
index 00000000..0dab1296
|
||||
--- /dev/null
|
||||
+++ b/include/jemalloc/internal/jemalloc_internal_defs_FreeBSD.h
|
||||
@@ -0,0 +1,9 @@
|
||||
+#ifndef __clang__
|
||||
+# undef JEMALLOC_INTERNAL_UNREACHABLE
|
||||
+# define JEMALLOC_INTERNAL_UNREACHABLE abort
|
||||
+
|
||||
+# undef JEMALLOC_C11_ATOMICS
|
||||
+# undef JEMALLOC_GCC_ATOMIC_ATOMICS
|
||||
+# undef JEMALLOC_GCC_U8_ATOMIC_ATOMICS
|
||||
+# undef JEMALLOC_GCC_U8_SYNC_ATOMICS
|
||||
+#endif
|
||||
diff --git a/include/jemalloc/internal/jemalloc_preamble.h.in b/include/jemalloc/internal/jemalloc_preamble.h.in
|
||||
index e621fbc8..dbdd5d6b 100644
|
||||
index 3418cbfa..53e30dc4 100644
|
||||
--- a/include/jemalloc/internal/jemalloc_preamble.h.in
|
||||
+++ b/include/jemalloc/internal/jemalloc_preamble.h.in
|
||||
@@ -8,6 +8,9 @@
|
||||
@ -107,10 +104,10 @@ index e621fbc8..dbdd5d6b 100644
|
||||
static const bool config_prof =
|
||||
#ifdef JEMALLOC_PROF
|
||||
diff --git a/include/jemalloc/internal/mutex.h b/include/jemalloc/internal/mutex.h
|
||||
index 6520c251..0013cbe9 100644
|
||||
index 7c24f072..94af1618 100644
|
||||
--- a/include/jemalloc/internal/mutex.h
|
||||
+++ b/include/jemalloc/internal/mutex.h
|
||||
@@ -121,9 +121,6 @@ struct malloc_mutex_s {
|
||||
@@ -135,9 +135,6 @@ struct malloc_mutex_s {
|
||||
|
||||
#ifdef JEMALLOC_LAZY_LOCK
|
||||
extern bool isthreaded;
|
||||
@ -120,7 +117,7 @@ index 6520c251..0013cbe9 100644
|
||||
#endif
|
||||
|
||||
bool malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
|
||||
@@ -131,6 +128,7 @@ bool malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
|
||||
@@ -145,6 +142,7 @@ bool malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
|
||||
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);
|
||||
@ -128,20 +125,38 @@ index 6520c251..0013cbe9 100644
|
||||
bool malloc_mutex_boot(void);
|
||||
void malloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex);
|
||||
|
||||
diff --git a/include/jemalloc/internal/test_hooks.h b/include/jemalloc/internal/test_hooks.h
|
||||
index a6351e59..0780c52f 100644
|
||||
--- a/include/jemalloc/internal/test_hooks.h
|
||||
+++ b/include/jemalloc/internal/test_hooks.h
|
||||
@@ -6,13 +6,6 @@ extern JEMALLOC_EXPORT void (*test_hooks_libc_hook)();
|
||||
|
||||
#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
|
||||
|
||||
-#define open JEMALLOC_HOOK(open, test_hooks_libc_hook)
|
||||
-#define read JEMALLOC_HOOK(read, test_hooks_libc_hook)
|
||||
-#define write JEMALLOC_HOOK(write, test_hooks_libc_hook)
|
||||
-#define readlink JEMALLOC_HOOK(readlink, test_hooks_libc_hook)
|
||||
-#define close JEMALLOC_HOOK(close, test_hooks_libc_hook)
|
||||
-#define creat JEMALLOC_HOOK(creat, test_hooks_libc_hook)
|
||||
-#define secure_getenv JEMALLOC_HOOK(secure_getenv, test_hooks_libc_hook)
|
||||
/* Note that this is undef'd and re-define'd in src/prof.c. */
|
||||
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
|
||||
|
||||
diff --git a/include/jemalloc/internal/tsd.h b/include/jemalloc/internal/tsd.h
|
||||
index 0b9841aa..f03eee61 100644
|
||||
index 9ba26004..ecfda5d6 100644
|
||||
--- a/include/jemalloc/internal/tsd.h
|
||||
+++ b/include/jemalloc/internal/tsd.h
|
||||
@@ -122,7 +122,8 @@ struct tsd_s {
|
||||
t use_a_getter_or_setter_instead_##n;
|
||||
@@ -198,7 +198,8 @@ struct tsd_s {
|
||||
t TSD_MANGLE(n);
|
||||
MALLOC_TSD
|
||||
#undef O
|
||||
-};
|
||||
+/* AddressSanitizer requires TLS data to be aligned to at least 8 bytes. */
|
||||
+} JEMALLOC_ALIGNED(16);
|
||||
|
||||
/*
|
||||
* Wrapper around tsd_t that makes it possible to avoid implicit conversion
|
||||
JEMALLOC_ALWAYS_INLINE uint8_t
|
||||
tsd_state_get(tsd_t *tsd) {
|
||||
diff --git a/include/jemalloc/jemalloc_FreeBSD.h b/include/jemalloc/jemalloc_FreeBSD.h
|
||||
new file mode 100644
|
||||
index 00000000..b752b0e7
|
||||
@ -345,10 +360,10 @@ index f9438912..47d032c1 100755
|
||||
+#include "jemalloc_FreeBSD.h"
|
||||
EOF
|
||||
diff --git a/src/jemalloc.c b/src/jemalloc.c
|
||||
index f93c16fa..e0ad297b 100644
|
||||
index ed13718d..fefb719a 100644
|
||||
--- a/src/jemalloc.c
|
||||
+++ b/src/jemalloc.c
|
||||
@@ -21,6 +21,10 @@
|
||||
@@ -23,6 +23,10 @@
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
|
||||
@ -359,10 +374,37 @@ index f93c16fa..e0ad297b 100644
|
||||
/* Runtime configuration options. */
|
||||
const char *je_malloc_conf
|
||||
#ifndef _WIN32
|
||||
@@ -3160,6 +3164,103 @@ je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
|
||||
@@ -2660,25 +2664,6 @@ je_realloc(void *ptr, size_t arg_size) {
|
||||
LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size);
|
||||
|
||||
if (unlikely(size == 0)) {
|
||||
- if (ptr != NULL) {
|
||||
- /* realloc(ptr, 0) is equivalent to free(ptr). */
|
||||
- UTRACE(ptr, 0, 0);
|
||||
- tcache_t *tcache;
|
||||
- tsd_t *tsd = tsd_fetch();
|
||||
- if (tsd_reentrancy_level_get(tsd) == 0) {
|
||||
- tcache = tcache_get(tsd);
|
||||
- } else {
|
||||
- tcache = NULL;
|
||||
- }
|
||||
-
|
||||
- uintptr_t args[3] = {(uintptr_t)ptr, size};
|
||||
- hook_invoke_dalloc(hook_dalloc_realloc, ptr, args);
|
||||
-
|
||||
- ifree(tsd, ptr, tcache, true);
|
||||
-
|
||||
- LOG("core.realloc.exit", "result: %p", NULL);
|
||||
- return NULL;
|
||||
- }
|
||||
size = 1;
|
||||
}
|
||||
|
||||
@@ -3750,6 +3735,103 @@ je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
|
||||
* End non-standard functions.
|
||||
*/
|
||||
/******************************************************************************/
|
||||
/*
|
||||
+/*
|
||||
+ * Begin compatibility functions.
|
||||
+ */
|
||||
+
|
||||
@ -459,11 +501,10 @@ index f93c16fa..e0ad297b 100644
|
||||
+ * End compatibility functions.
|
||||
+ */
|
||||
+/******************************************************************************/
|
||||
+/*
|
||||
/*
|
||||
* The following functions are used by threading libraries for protection of
|
||||
* malloc during fork().
|
||||
*/
|
||||
@@ -3323,4 +3424,11 @@ jemalloc_postfork_child(void) {
|
||||
@@ -3919,4 +4001,11 @@ jemalloc_postfork_child(void) {
|
||||
ctl_postfork_child(tsd_tsdn(tsd));
|
||||
}
|
||||
|
||||
@ -476,7 +517,7 @@ index f93c16fa..e0ad297b 100644
|
||||
+
|
||||
/******************************************************************************/
|
||||
diff --git a/src/malloc_io.c b/src/malloc_io.c
|
||||
index 7bdc13f9..c8802c70 100644
|
||||
index d7cb0f52..cda589c4 100644
|
||||
--- a/src/malloc_io.c
|
||||
+++ b/src/malloc_io.c
|
||||
@@ -75,6 +75,20 @@ wrtmessage(void *cbopaque, const char *s) {
|
||||
@ -501,7 +542,7 @@ index 7bdc13f9..c8802c70 100644
|
||||
* Wrapper around malloc_message() that avoids the need for
|
||||
* je_malloc_message(...) throughout the code.
|
||||
diff --git a/src/mutex.c b/src/mutex.c
|
||||
index 30222b3e..b2c36283 100644
|
||||
index 3f920f5b..88a7730c 100644
|
||||
--- a/src/mutex.c
|
||||
+++ b/src/mutex.c
|
||||
@@ -41,6 +41,17 @@ pthread_create(pthread_t *__restrict thread,
|
||||
@ -523,9 +564,10 @@ index 30222b3e..b2c36283 100644
|
||||
|
||||
void
|
||||
@@ -131,6 +142,16 @@ mutex_addr_comp(const witness_t *witness1, void *mutex1,
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
+bool
|
||||
+malloc_mutex_first_thread(void) {
|
||||
+
|
||||
+#ifndef JEMALLOC_MUTEX_INIT_CB
|
||||
@ -535,7 +577,6 @@ index 30222b3e..b2c36283 100644
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
+bool
|
||||
bool
|
||||
malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
|
||||
witness_rank_t rank, malloc_mutex_lock_order_t lock_order) {
|
||||
mutex_prof_data_init(&mutex->prof_data);
|
||||
|
@ -1 +1 @@
|
||||
5.1.0-0-g61efbda7098de6fe64c362d309824864308c36d4
|
||||
5.2.1-0-gea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
|
@ -1,13 +1,13 @@
|
||||
'\" t
|
||||
.\" Title: JEMALLOC
|
||||
.\" Author: Jason Evans
|
||||
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
|
||||
.\" Date: 05/08/2018
|
||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||
.\" Date: 11/10/2019
|
||||
.\" Manual: User Manual
|
||||
.\" Source: jemalloc 5.1.0-0-g61efbda7098de6fe64c362d309824864308c36d4
|
||||
.\" Source: jemalloc 5.2.1-0-gea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "JEMALLOC" "3" "05/08/2018" "jemalloc 5.1.0-0-g61efbda7098d" "User Manual"
|
||||
.TH "JEMALLOC" "3" "11/10/2019" "jemalloc 5.2.1-0-gea6b3e973b47" "User Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@ -31,7 +31,7 @@
|
||||
jemalloc \- general purpose memory allocation functions
|
||||
.SH "LIBRARY"
|
||||
.PP
|
||||
This manual describes jemalloc 5\&.1\&.0\-0\-g61efbda7098de6fe64c362d309824864308c36d4\&. More information can be found at the
|
||||
This manual describes jemalloc 5\&.2\&.1\-0\-gea6b3e973b477b8061e0076bb257dbd7f3faa756\&. More information can be found at the
|
||||
\m[blue]\fBjemalloc website\fR\m[]\&\s-2\u[1]\d\s+2\&.
|
||||
.PP
|
||||
The following configuration options are enabled in libc\*(Aqs built\-in jemalloc:
|
||||
@ -396,7 +396,7 @@ string, in which case the statistics are presented in
|
||||
as a character within the
|
||||
\fIopts\fR
|
||||
string\&. Note that
|
||||
malloc_message()
|
||||
malloc_stats_print()
|
||||
uses the
|
||||
mallctl*()
|
||||
functions internally, so inconsistent statistics can be reported if multiple threads use these functions simultaneously\&. If
|
||||
@ -411,7 +411,9 @@ and
|
||||
\(lql\(rq
|
||||
can be specified to omit per size class statistics for bins and large objects, respectively;
|
||||
\(lqx\(rq
|
||||
can be specified to omit all mutex statistics\&. 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\&.
|
||||
can be specified to omit all mutex statistics;
|
||||
\(lqe\(rq
|
||||
can be used to omit extent statistics\&. 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\&.
|
||||
.PP
|
||||
The
|
||||
malloc_usable_size()
|
||||
@ -826,6 +828,17 @@ in these cases\&. This option is disabled by default unless
|
||||
is specified during configuration, in which case it is enabled by default\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.confirm_conf (\fBbool\fR) r\-
|
||||
.RS 4
|
||||
Confirm\-runtime\-options\-when\-program\-starts enabled/disabled\&. If true, the string specified via
|
||||
\fB\-\-with\-malloc\-conf\fR, the string pointed to by the global variable
|
||||
\fImalloc_conf\fR, the
|
||||
\(lqname\(rq
|
||||
of the file referenced by the symbolic link named
|
||||
/etc/malloc\&.conf, and the value of the environment variable
|
||||
\fBMALLOC_CONF\fR, will be printed in order\&. Then, each option being set will be individually printed\&. This option is disabled by default\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.abort_conf (\fBbool\fR) r\-
|
||||
.RS 4
|
||||
Abort\-on\-invalid\-configuration enabled/disabled\&. If true, invalid runtime options are fatal\&. The process will call
|
||||
@ -852,9 +865,10 @@ If true, retain unused virtual memory for later reuse rather than discarding it
|
||||
\fBmunmap\fR(2)
|
||||
or equivalent (see
|
||||
stats\&.retained
|
||||
for related details)\&. This option is disabled by default unless discarding virtual memory is known to trigger platform\-specific performance problems, e\&.g\&. for [64\-bit] Linux, which has a quirk in its virtual memory allocation algorithm that causes semi\-permanent VM map holes under normal jemalloc operation\&. Although
|
||||
\fBmunmap\fR(2)
|
||||
causes issues on 32\-bit Linux as well, retaining virtual memory for 32\-bit Linux is disabled by default due to the practical possibility of address space exhaustion\&.
|
||||
for related details)\&. It also makes jemalloc use
|
||||
\fBmmap\fR(2)
|
||||
or equivalent in a more greedy way, mapping larger chunks in one go\&. This option is disabled by default unless discarding virtual memory is known to trigger platform\-specific performance problems, namely 1) for [64\-bit] Linux, which has a quirk in its virtual memory allocation algorithm that causes semi\-permanent VM map holes under normal jemalloc operation; and 2) for [64\-bit] Windows, which disallows split / merged regions with
|
||||
\fI\fBMEM_RELEASE\fR\fR\&. Although the same issues may present on 32\-bit platforms as well, retaining virtual memory for 32\-bit Linux and Windows is disabled by default due to the practical possibility of address space exhaustion\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.dss (\fBconst char *\fR) r\-
|
||||
@ -882,6 +896,13 @@ opt\&.narenas (\fBunsigned\fR) r\-
|
||||
Maximum number of arenas to use for automatic multiplexing of threads and arenas\&. The default is four times the number of CPUs, or one if there is a single CPU\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.oversize_threshold (\fBsize_t\fR) r\-
|
||||
.RS 4
|
||||
The threshold in bytes of which requests are considered oversize\&. Allocation requests with greater sizes are fulfilled from a dedicated arena (automatically managed, however not within
|
||||
narenas), in order to reduce fragmentation by not mixing huge allocations with small ones\&. In addition, the decay API guarantees on the extents greater than the specified threshold may be overridden\&. Note that requests with arena index specified via
|
||||
\fBMALLOCX_ARENA\fR, or threads associated with explicit arenas will not be considered\&. The default threshold is 8MiB\&. Values not within large size classes disables this feature\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.percpu_arena (\fBconst char *\fR) r\-
|
||||
.RS 4
|
||||
Per CPU arena mode\&. Use the
|
||||
@ -893,14 +914,14 @@ setting uses one arena per physical CPU, which means the two hyper threads on th
|
||||
\(lqdisabled\(rq\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.background_thread (\fBconst bool\fR) r\-
|
||||
opt\&.background_thread (\fBbool\fR) r\-
|
||||
.RS 4
|
||||
Internal background worker threads enabled/disabled\&. Because of potential circular dependencies, enabling background thread using this option may cause crash or deadlock during initialization\&. For a reliable way to use this feature, see
|
||||
background_thread
|
||||
for dynamic control options and details\&. This option is disabled by default\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.max_background_threads (\fBconst size_t\fR) r\-
|
||||
opt\&.max_background_threads (\fBsize_t\fR) r\-
|
||||
.RS 4
|
||||
Maximum number of background threads that will be created if
|
||||
background_thread
|
||||
@ -917,7 +938,9 @@ and
|
||||
arena\&.<i>\&.dirty_decay_ms
|
||||
for related dynamic control options\&. See
|
||||
opt\&.muzzy_decay_ms
|
||||
for a description of muzzy pages\&.
|
||||
for a description of muzzy pages\&.for a description of muzzy pages\&. Note that when the
|
||||
oversize_threshold
|
||||
feature is enabled, the arenas reserved for oversize requests may have its own default decay settings\&.
|
||||
.RE
|
||||
.PP
|
||||
opt\&.muzzy_decay_ms (\fBssize_t\fR) r\-
|
||||
@ -1293,7 +1316,7 @@ arena\&.<i>\&.extent_hooks (\fBextent_hooks_t *\fR) rw
|
||||
.RS 4
|
||||
Get or set the extent management hook functions for arena <i>\&. The functions must be capable of operating on all extant extents associated with arena <i>, usually by passing unknown extents to the replaced functions\&. In practice, it is feasible to control allocation for arenas explicitly created via
|
||||
arenas\&.create
|
||||
such that all extents originate from an application\-supplied extent allocator (by specifying the custom extent hook functions during arena creation), but the automatically created arenas will have already created extents prior to the application having an opportunity to take over extent allocation\&.
|
||||
such that all extents originate from an application\-supplied extent allocator (by specifying the custom extent hook functions during arena creation)\&. However, the API guarantees for the automatically created arenas may be relaxed \-\- hooks set there may be called in a "best effort" fashion; in addition there may be extents created prior to the application having an opportunity to take over extent allocation\&.
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
@ -1876,6 +1899,11 @@ stats\&.retained
|
||||
for details\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.extent_avail (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Number of allocated (but unused) extent structs in this arena\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.base (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Number of bytes dedicated to bootstrap\-sensitive allocator metadata structures\&.
|
||||
@ -1956,6 +1984,16 @@ stats\&.arenas\&.<i>\&.small\&.nrequests (\fBuint64_t\fR) r\- [\fB\-\-enable\-st
|
||||
Cumulative number of allocation requests satisfied by all bin size classes\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.small\&.nfills (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Cumulative number of tcache fills by all small size classes\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.small\&.nflushes (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Cumulative number of tcache flushes by all small size classes\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.large\&.allocated (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Number of bytes currently allocated by large objects\&.
|
||||
@ -1980,6 +2018,16 @@ stats\&.arenas\&.<i>\&.large\&.nrequests (\fBuint64_t\fR) r\- [\fB\-\-enable\-st
|
||||
Cumulative number of allocation requests satisfied by all large size classes\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.large\&.nfills (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Cumulative number of tcache fills by all large size classes\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.large\&.nflushes (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Cumulative number of tcache flushes by all large size classes\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.bins\&.<j>\&.nmalloc (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Cumulative number of times a bin region of the corresponding size class was allocated from the arena, whether to fill the relevant tcache if
|
||||
@ -2029,6 +2077,11 @@ stats\&.arenas\&.<i>\&.bins\&.<j>\&.curslabs (\fBsize_t\fR) r\- [\fB\-\-enable\-
|
||||
Current number of slabs\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.bins\&.<j>\&.nonfull_slabs (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Current number of nonfull slabs\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.bins\&.<j>\&.mutex\&.{counter} (\fBcounter specific type\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Statistics on
|
||||
@ -2039,6 +2092,16 @@ is one of the counters in
|
||||
mutex profiling counters\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.extents\&.<j>\&.n{extent_type} (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Number of extents of the given type in this arena in the bucket corresponding to page size index <j>\&. The extent type is one of dirty, muzzy, or retained\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.extents\&.<j>\&.{extent_type}_bytes (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Sum of the bytes managed by extents of the given type in this arena in the bucket corresponding to page size index <j>\&. The extent type is one of dirty, muzzy, or retained\&.
|
||||
.RE
|
||||
.PP
|
||||
stats\&.arenas\&.<i>\&.lextents\&.<j>\&.nmalloc (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR]
|
||||
.RS 4
|
||||
Cumulative number of times a large extent of the corresponding size class was allocated from the arena, whether to fill the relevant tcache if
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
#include "jemalloc/internal/bin.h"
|
||||
#include "jemalloc/internal/extent_dss.h"
|
||||
#include "jemalloc/internal/hook.h"
|
||||
#include "jemalloc/internal/pages.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
|
||||
extern ssize_t opt_dirty_decay_ms;
|
||||
@ -16,13 +16,17 @@ extern const char *percpu_arena_mode_names[];
|
||||
extern const uint64_t h_steps[SMOOTHSTEP_NSTEPS];
|
||||
extern malloc_mutex_t arenas_lock;
|
||||
|
||||
extern size_t opt_oversize_threshold;
|
||||
extern size_t oversize_threshold;
|
||||
|
||||
void arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,
|
||||
unsigned *nthreads, const char **dss, ssize_t *dirty_decay_ms,
|
||||
ssize_t *muzzy_decay_ms, size_t *nactive, size_t *ndirty, size_t *nmuzzy);
|
||||
void arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
|
||||
size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,
|
||||
bin_stats_t *bstats, arena_stats_large_t *lstats);
|
||||
bin_stats_t *bstats, arena_stats_large_t *lstats,
|
||||
arena_stats_extents_t *estats);
|
||||
void arena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent);
|
||||
#ifdef JEMALLOC_JET
|
||||
@ -56,16 +60,17 @@ 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_promote(tsdn_t *tsdn, const void *ptr, size_t usize);
|
||||
void arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize);
|
||||
void arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
bool slow_path);
|
||||
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_t *extent, void *ptr);
|
||||
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
szind_t binind, extent_t *extent, void *ptr);
|
||||
void arena_dalloc_small(tsdn_t *tsdn, void *ptr);
|
||||
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, bool zero);
|
||||
size_t extra, bool zero, size_t *newsize);
|
||||
void *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
||||
size_t size, size_t alignment, bool zero, tcache_t *tcache);
|
||||
size_t size, size_t alignment, bool zero, tcache_t *tcache,
|
||||
hook_ralloc_args_t *hook_args);
|
||||
dss_prec_t arena_dss_prec_get(arena_t *arena);
|
||||
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
|
||||
ssize_t arena_dirty_decay_ms_default_get(void);
|
||||
@ -79,7 +84,12 @@ void arena_nthreads_inc(arena_t *arena, bool internal);
|
||||
void arena_nthreads_dec(arena_t *arena, bool internal);
|
||||
size_t arena_extent_sn_next(arena_t *arena);
|
||||
arena_t *arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
|
||||
void arena_boot(void);
|
||||
bool arena_init_huge(void);
|
||||
bool arena_is_huge(unsigned arena_ind);
|
||||
arena_t *arena_choose_huge(tsd_t *tsd);
|
||||
bin_t *arena_bin_choose_lock(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
unsigned *binshard);
|
||||
void arena_boot(sc_data_t *sc_data);
|
||||
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);
|
||||
|
@ -4,10 +4,36 @@
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/rtree.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
arena_has_default_hooks(arena_t *arena) {
|
||||
return (extent_hooks_get(arena) == &extent_hooks_default);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE arena_t *
|
||||
arena_choose_maybe_huge(tsd_t *tsd, arena_t *arena, size_t size) {
|
||||
if (arena != NULL) {
|
||||
return arena;
|
||||
}
|
||||
|
||||
/*
|
||||
* For huge allocations, use the dedicated huge arena if both are true:
|
||||
* 1) is using auto arena selection (i.e. arena == NULL), and 2) the
|
||||
* thread is not assigned to a manual arena.
|
||||
*/
|
||||
if (unlikely(size >= oversize_threshold)) {
|
||||
arena_t *tsd_arena = tsd_arena_get(tsd);
|
||||
if (tsd_arena == NULL || arena_is_auto(tsd_arena)) {
|
||||
return arena_choose_huge(tsd);
|
||||
}
|
||||
}
|
||||
|
||||
return arena_choose(tsd, NULL);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE prof_tctx_t *
|
||||
arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
|
||||
cassert(config_prof);
|
||||
@ -28,7 +54,7 @@ arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, UNUSED size_t usize,
|
||||
arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
@ -47,7 +73,7 @@ arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, UNUSED size_t usize,
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, UNUSED prof_tctx_t *tctx) {
|
||||
arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
@ -57,6 +83,32 @@ arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, UNUSED prof_tctx_t *tctx) {
|
||||
large_prof_tctx_reset(tsdn, extent);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE nstime_t
|
||||
arena_prof_alloc_time_get(tsdn_t *tsdn, const void *ptr,
|
||||
alloc_ctx_t *alloc_ctx) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
/*
|
||||
* Unlike arena_prof_prof_tctx_{get, set}, we only call this once we're
|
||||
* sure we have a sampled allocation.
|
||||
*/
|
||||
assert(!extent_slab_get(extent));
|
||||
return large_prof_alloc_time_get(extent);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_prof_alloc_time_set(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx,
|
||||
nstime_t t) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
assert(!extent_slab_get(extent));
|
||||
large_prof_alloc_time_set(extent, t);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks) {
|
||||
tsd_t *tsd;
|
||||
@ -83,14 +135,33 @@ arena_decay_tick(tsdn_t *tsdn, arena_t *arena) {
|
||||
arena_decay_ticks(tsdn, arena, 1);
|
||||
}
|
||||
|
||||
/* Purge a single extent to retained / unmapped directly. */
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_decay_extent(tsdn_t *tsdn,arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
extent_t *extent) {
|
||||
size_t extent_size = extent_size_get(extent);
|
||||
extent_dalloc_wrapper(tsdn, arena,
|
||||
r_extent_hooks, extent);
|
||||
if (config_stats) {
|
||||
/* Update stats accordingly. */
|
||||
arena_stats_lock(tsdn, &arena->stats);
|
||||
arena_stats_add_u64(tsdn, &arena->stats,
|
||||
&arena->decay_dirty.stats->nmadvise, 1);
|
||||
arena_stats_add_u64(tsdn, &arena->stats,
|
||||
&arena->decay_dirty.stats->purged, extent_size >> LG_PAGE);
|
||||
arena_stats_sub_zu(tsdn, &arena->stats, &arena->stats.mapped,
|
||||
extent_size);
|
||||
arena_stats_unlock(tsdn, &arena->stats);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
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)) {
|
||||
if (likely(size <= SC_SMALL_MAXCLASS)) {
|
||||
return tcache_alloc_small(tsdn_tsd(tsdn), arena,
|
||||
tcache, size, ind, zero, slow_path);
|
||||
}
|
||||
@ -119,7 +190,7 @@ arena_salloc(tsdn_t *tsdn, const void *ptr) {
|
||||
|
||||
szind_t szind = rtree_szind_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true);
|
||||
assert(szind != NSIZES);
|
||||
assert(szind != SC_NSIZES);
|
||||
|
||||
return sz_index2size(szind);
|
||||
}
|
||||
@ -152,11 +223,21 @@ arena_vsalloc(tsdn_t *tsdn, const void *ptr) {
|
||||
/* Only slab members should be looked up via interior pointers. */
|
||||
assert(extent_addr_get(extent) == ptr || extent_slab_get(extent));
|
||||
|
||||
assert(szind != NSIZES);
|
||||
assert(szind != SC_NSIZES);
|
||||
|
||||
return sz_index2size(szind);
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_dalloc_large_no_tcache(tsdn_t *tsdn, void *ptr, szind_t szind) {
|
||||
if (config_prof && unlikely(szind < SC_NBINS)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, NULL, true);
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) {
|
||||
assert(ptr != NULL);
|
||||
@ -173,13 +254,28 @@ arena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) {
|
||||
extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
|
||||
rtree_ctx, (uintptr_t)ptr, true);
|
||||
assert(szind == extent_szind_get(extent));
|
||||
assert(szind < NSIZES);
|
||||
assert(szind < SC_NSIZES);
|
||||
assert(slab == extent_slab_get(extent));
|
||||
}
|
||||
|
||||
if (likely(slab)) {
|
||||
/* Small allocation. */
|
||||
arena_dalloc_small(tsdn, ptr);
|
||||
} else {
|
||||
arena_dalloc_large_no_tcache(tsdn, ptr, szind);
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_dalloc_large(tsdn_t *tsdn, void *ptr, tcache_t *tcache, szind_t szind,
|
||||
bool slow_path) {
|
||||
if (szind < nhbins) {
|
||||
if (config_prof && unlikely(szind < SC_NBINS)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, tcache, slow_path);
|
||||
} else {
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr, szind,
|
||||
slow_path);
|
||||
}
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
@ -203,7 +299,7 @@ arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
if (alloc_ctx != NULL) {
|
||||
szind = alloc_ctx->szind;
|
||||
slab = alloc_ctx->slab;
|
||||
assert(szind != NSIZES);
|
||||
assert(szind != SC_NSIZES);
|
||||
} else {
|
||||
rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
|
||||
rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
|
||||
@ -215,7 +311,7 @@ arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
|
||||
rtree_ctx, (uintptr_t)ptr, true);
|
||||
assert(szind == extent_szind_get(extent));
|
||||
assert(szind < NSIZES);
|
||||
assert(szind < SC_NSIZES);
|
||||
assert(slab == extent_slab_get(extent));
|
||||
}
|
||||
|
||||
@ -224,25 +320,14 @@ arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
|
||||
slow_path);
|
||||
} else {
|
||||
if (szind < nhbins) {
|
||||
if (config_prof && unlikely(szind < NBINS)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, tcache,
|
||||
slow_path);
|
||||
} else {
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,
|
||||
szind, slow_path);
|
||||
}
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
}
|
||||
arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {
|
||||
assert(ptr != NULL);
|
||||
assert(size <= LARGE_MAXCLASS);
|
||||
assert(size <= SC_LARGE_MAXCLASS);
|
||||
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
@ -252,7 +337,7 @@ arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {
|
||||
* object, so base szind and slab on the given size.
|
||||
*/
|
||||
szind = sz_size2index(size);
|
||||
slab = (szind < NBINS);
|
||||
slab = (szind < SC_NBINS);
|
||||
}
|
||||
|
||||
if ((config_prof && opt_prof) || config_debug) {
|
||||
@ -264,7 +349,7 @@ arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {
|
||||
(uintptr_t)ptr, true, &szind, &slab);
|
||||
|
||||
assert(szind == sz_size2index(size));
|
||||
assert((config_prof && opt_prof) || slab == (szind < NBINS));
|
||||
assert((config_prof && opt_prof) || slab == (szind < SC_NBINS));
|
||||
|
||||
if (config_debug) {
|
||||
extent_t *extent = rtree_extent_read(tsdn,
|
||||
@ -278,8 +363,7 @@ arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {
|
||||
/* Small allocation. */
|
||||
arena_dalloc_small(tsdn, ptr);
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
arena_dalloc_large_no_tcache(tsdn, ptr, szind);
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +372,7 @@ arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
alloc_ctx_t *alloc_ctx, bool slow_path) {
|
||||
assert(!tsdn_null(tsdn) || tcache == NULL);
|
||||
assert(ptr != NULL);
|
||||
assert(size <= LARGE_MAXCLASS);
|
||||
assert(size <= SC_LARGE_MAXCLASS);
|
||||
|
||||
if (unlikely(tcache == NULL)) {
|
||||
arena_sdalloc_no_tcache(tsdn, ptr, size);
|
||||
@ -297,7 +381,7 @@ arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
|
||||
szind_t szind;
|
||||
bool slab;
|
||||
UNUSED alloc_ctx_t local_ctx;
|
||||
alloc_ctx_t local_ctx;
|
||||
if (config_prof && opt_prof) {
|
||||
if (alloc_ctx == NULL) {
|
||||
/* Uncommon case and should be a static check. */
|
||||
@ -318,7 +402,7 @@ arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
* object, so base szind and slab on the given size.
|
||||
*/
|
||||
szind = sz_size2index(size);
|
||||
slab = (szind < NBINS);
|
||||
slab = (szind < SC_NBINS);
|
||||
}
|
||||
|
||||
if (config_debug) {
|
||||
@ -336,18 +420,7 @@ arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
|
||||
slow_path);
|
||||
} else {
|
||||
if (szind < nhbins) {
|
||||
if (config_prof && unlikely(szind < NBINS)) {
|
||||
arena_dalloc_promoted(tsdn, ptr, tcache,
|
||||
slow_path);
|
||||
} else {
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn),
|
||||
tcache, ptr, szind, slow_path);
|
||||
}
|
||||
} else {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
large_dalloc(tsdn, extent);
|
||||
}
|
||||
arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,9 @@
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/mutex_prof.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
|
||||
/*
|
||||
* In those architectures that support 64-bit atomics, we use atomic updates for
|
||||
@ -33,6 +35,13 @@ struct arena_stats_large_s {
|
||||
* periodically merges into this counter.
|
||||
*/
|
||||
arena_stats_u64_t nrequests; /* Partially derived. */
|
||||
/*
|
||||
* Number of tcache fills / flushes for large (similarly, periodically
|
||||
* merged). Note that there is no large tcache batch-fill currently
|
||||
* (i.e. only fill 1 at a time); however flush may be batched.
|
||||
*/
|
||||
arena_stats_u64_t nfills; /* Partially derived. */
|
||||
arena_stats_u64_t nflushes; /* Partially derived. */
|
||||
|
||||
/* Current number of allocations of this size class. */
|
||||
size_t curlextents; /* Derived. */
|
||||
@ -48,6 +57,22 @@ struct arena_stats_decay_s {
|
||||
arena_stats_u64_t purged;
|
||||
};
|
||||
|
||||
typedef struct arena_stats_extents_s arena_stats_extents_t;
|
||||
struct arena_stats_extents_s {
|
||||
/*
|
||||
* Stats for a given index in the range [0, SC_NPSIZES] in an extents_t.
|
||||
* We track both bytes and # of extents: two extents in the same bucket
|
||||
* may have different sizes if adjacent size classes differ by more than
|
||||
* a page, so bytes cannot always be derived from # of extents.
|
||||
*/
|
||||
atomic_zu_t ndirty;
|
||||
atomic_zu_t dirty_bytes;
|
||||
atomic_zu_t nmuzzy;
|
||||
atomic_zu_t muzzy_bytes;
|
||||
atomic_zu_t nretained;
|
||||
atomic_zu_t retained_bytes;
|
||||
};
|
||||
|
||||
/*
|
||||
* Arena stats. Note that fields marked "derived" are not directly maintained
|
||||
* within the arena code; rather their values are derived during stats merge
|
||||
@ -69,6 +94,9 @@ struct arena_stats_s {
|
||||
*/
|
||||
atomic_zu_t retained; /* Derived. */
|
||||
|
||||
/* Number of extent_t structs allocated by base, but not being used. */
|
||||
atomic_zu_t extent_avail;
|
||||
|
||||
arena_stats_decay_t decay_dirty;
|
||||
arena_stats_decay_t decay_muzzy;
|
||||
|
||||
@ -80,22 +108,27 @@ struct arena_stats_s {
|
||||
atomic_zu_t allocated_large; /* Derived. */
|
||||
arena_stats_u64_t nmalloc_large; /* Derived. */
|
||||
arena_stats_u64_t ndalloc_large; /* Derived. */
|
||||
arena_stats_u64_t nfills_large; /* Derived. */
|
||||
arena_stats_u64_t nflushes_large; /* Derived. */
|
||||
arena_stats_u64_t nrequests_large; /* Derived. */
|
||||
|
||||
/* VM space had to be leaked (undocumented). Normally 0. */
|
||||
atomic_zu_t abandoned_vm;
|
||||
|
||||
/* Number of bytes cached in tcache associated with this arena. */
|
||||
atomic_zu_t tcache_bytes; /* Derived. */
|
||||
|
||||
mutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes];
|
||||
|
||||
/* One element for each large size class. */
|
||||
arena_stats_large_t lstats[NSIZES - NBINS];
|
||||
arena_stats_large_t lstats[SC_NSIZES - SC_NBINS];
|
||||
|
||||
/* Arena uptime. */
|
||||
nstime_t uptime;
|
||||
};
|
||||
|
||||
static inline bool
|
||||
arena_stats_init(UNUSED tsdn_t *tsdn, arena_stats_t *arena_stats) {
|
||||
arena_stats_init(tsdn_t *tsdn, arena_stats_t *arena_stats) {
|
||||
if (config_debug) {
|
||||
for (size_t i = 0; i < sizeof(arena_stats_t); i++) {
|
||||
assert(((char *)arena_stats)[i] == 0);
|
||||
@ -147,11 +180,11 @@ arena_stats_add_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
#endif
|
||||
}
|
||||
|
||||
UNUSED static inline void
|
||||
static inline void
|
||||
arena_stats_sub_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
arena_stats_u64_t *p, uint64_t x) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
UNUSED uint64_t r = atomic_fetch_sub_u64(p, x, ATOMIC_RELAXED);
|
||||
uint64_t r = atomic_fetch_sub_u64(p, x, ATOMIC_RELAXED);
|
||||
assert(r - x <= r);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
@ -176,7 +209,8 @@ arena_stats_accum_u64(arena_stats_u64_t *dst, uint64_t src) {
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
arena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p) {
|
||||
arena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
atomic_zu_t *p) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
return atomic_load_zu(p, ATOMIC_RELAXED);
|
||||
#else
|
||||
@ -186,8 +220,8 @@ arena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,
|
||||
size_t x) {
|
||||
arena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
atomic_zu_t *p, size_t x) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
atomic_fetch_add_zu(p, x, ATOMIC_RELAXED);
|
||||
#else
|
||||
@ -198,10 +232,10 @@ arena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_sub_zu(tsdn_t *tsdn, arena_stats_t *arena_stats, atomic_zu_t *p,
|
||||
size_t x) {
|
||||
arena_stats_sub_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
atomic_zu_t *p, size_t x) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
UNUSED size_t r = atomic_fetch_sub_zu(p, x, ATOMIC_RELAXED);
|
||||
size_t r = atomic_fetch_sub_zu(p, x, ATOMIC_RELAXED);
|
||||
assert(r - x <= r);
|
||||
#else
|
||||
malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
|
||||
@ -218,11 +252,12 @@ arena_stats_accum_zu(atomic_zu_t *dst, size_t src) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
arena_stats_large_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
arena_stats_large_flush_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,
|
||||
szind_t szind, uint64_t nrequests) {
|
||||
arena_stats_lock(tsdn, arena_stats);
|
||||
arena_stats_add_u64(tsdn, arena_stats, &arena_stats->lstats[szind -
|
||||
NBINS].nrequests, nrequests);
|
||||
arena_stats_large_t *lstats = &arena_stats->lstats[szind - SC_NBINS];
|
||||
arena_stats_add_u64(tsdn, arena_stats, &lstats->nrequests, nrequests);
|
||||
arena_stats_add_u64(tsdn, arena_stats, &lstats->nflushes, 1);
|
||||
arena_stats_unlock(tsdn, arena_stats);
|
||||
}
|
||||
|
||||
@ -233,5 +268,4 @@ arena_stats_mapped_add(tsdn_t *tsdn, arena_stats_t *arena_stats, size_t size) {
|
||||
arena_stats_unlock(tsdn, arena_stats);
|
||||
}
|
||||
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_STATS_H */
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/nstime.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/smoothstep.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
|
||||
@ -90,6 +90,9 @@ struct arena_s {
|
||||
*/
|
||||
atomic_u_t nthreads[2];
|
||||
|
||||
/* Next bin shard for binding new threads. Synchronization: atomic. */
|
||||
atomic_u_t binshard_next;
|
||||
|
||||
/*
|
||||
* When percpu_arena is enabled, to amortize the cost of reading /
|
||||
* updating the current CPU id, track the most recent thread accessing
|
||||
@ -113,7 +116,6 @@ struct arena_s {
|
||||
|
||||
/* Synchronization: internal. */
|
||||
prof_accum_t prof_accum;
|
||||
uint64_t prof_accumbytes;
|
||||
|
||||
/*
|
||||
* PRNG state for cache index randomization of large allocation base
|
||||
@ -196,6 +198,7 @@ struct arena_s {
|
||||
* Synchronization: extent_avail_mtx.
|
||||
*/
|
||||
extent_tree_t extent_avail;
|
||||
atomic_zu_t extent_avail_cnt;
|
||||
malloc_mutex_t extent_avail_mtx;
|
||||
|
||||
/*
|
||||
@ -203,7 +206,7 @@ struct arena_s {
|
||||
*
|
||||
* Synchronization: internal.
|
||||
*/
|
||||
bin_t bins[NBINS];
|
||||
bins_t bins[SC_NBINS];
|
||||
|
||||
/*
|
||||
* Base allocator, from which arena metadata are allocated.
|
||||
|
@ -1,13 +1,15 @@
|
||||
#ifndef JEMALLOC_INTERNAL_ARENA_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_ARENA_TYPES_H
|
||||
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/* Maximum number of regions in one slab. */
|
||||
#define LG_SLAB_MAXREGS (LG_PAGE - LG_TINY_MIN)
|
||||
#define LG_SLAB_MAXREGS (LG_PAGE - SC_LG_TINY_MIN)
|
||||
#define SLAB_MAXREGS (1U << LG_SLAB_MAXREGS)
|
||||
|
||||
/* Default decay times in milliseconds. */
|
||||
#define DIRTY_DECAY_MS_DEFAULT ZD(10 * 1000)
|
||||
#define MUZZY_DECAY_MS_DEFAULT ZD(10 * 1000)
|
||||
#define MUZZY_DECAY_MS_DEFAULT (0)
|
||||
/* Number of event ticks between time checks. */
|
||||
#define DECAY_NTICKS_PER_UPDATE 1000
|
||||
|
||||
@ -40,4 +42,10 @@ typedef enum {
|
||||
#define PERCPU_ARENA_ENABLED(m) ((m) >= percpu_arena_mode_enabled_base)
|
||||
#define PERCPU_ARENA_DEFAULT percpu_arena_disabled
|
||||
|
||||
/*
|
||||
* When allocation_size >= oversize_threshold, use the dedicated huge arena
|
||||
* (unless have explicitly spicified arena index). 0 disables the feature.
|
||||
*/
|
||||
#define OVERSIZE_THRESHOLD_DEFAULT (8 << 20)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_ARENA_TYPES_H */
|
||||
|
@ -1,12 +1,19 @@
|
||||
#ifndef JEMALLOC_INTERNAL_ATOMIC_H
|
||||
#define JEMALLOC_INTERNAL_ATOMIC_H
|
||||
|
||||
#define ATOMIC_INLINE static inline
|
||||
#define ATOMIC_INLINE JEMALLOC_ALWAYS_INLINE
|
||||
|
||||
#define JEMALLOC_U8_ATOMICS
|
||||
#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS)
|
||||
# include "jemalloc/internal/atomic_gcc_atomic.h"
|
||||
# if !defined(JEMALLOC_GCC_U8_ATOMIC_ATOMICS)
|
||||
# undef JEMALLOC_U8_ATOMICS
|
||||
# endif
|
||||
#elif defined(JEMALLOC_GCC_SYNC_ATOMICS)
|
||||
# include "jemalloc/internal/atomic_gcc_sync.h"
|
||||
# if !defined(JEMALLOC_GCC_U8_SYNC_ATOMICS)
|
||||
# undef JEMALLOC_U8_ATOMICS
|
||||
# endif
|
||||
#elif defined(_MSC_VER)
|
||||
# include "jemalloc/internal/atomic_msvc.h"
|
||||
#elif defined(JEMALLOC_C11_ATOMICS)
|
||||
@ -66,6 +73,8 @@ JEMALLOC_GENERATE_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)
|
||||
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
|
||||
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(uint8_t, u8, 0)
|
||||
|
||||
JEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)
|
||||
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
|
@ -67,7 +67,8 @@ atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
UNUSED type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
return __atomic_compare_exchange(&a->repr, expected, &desired, \
|
||||
true, atomic_enum_to_builtin(success_mo), \
|
||||
@ -76,7 +77,8 @@ atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
||||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
UNUSED type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
return __atomic_compare_exchange(&a->repr, expected, &desired, \
|
||||
false, \
|
||||
|
@ -27,8 +27,10 @@ atomic_fence(atomic_memory_order_t mo) {
|
||||
asm volatile("" ::: "memory");
|
||||
# if defined(__i386__) || defined(__x86_64__)
|
||||
/* This is implicit on x86. */
|
||||
# elif defined(__ppc__)
|
||||
# elif defined(__ppc64__)
|
||||
asm volatile("lwsync");
|
||||
# elif defined(__ppc__)
|
||||
asm volatile("sync");
|
||||
# elif defined(__sparc__) && defined(__arch64__)
|
||||
if (mo == atomic_memory_order_acquire) {
|
||||
asm volatile("membar #LoadLoad | #LoadStore");
|
||||
@ -113,8 +115,8 @@ atomic_store_##short_type(atomic_##short_type##_t *a, \
|
||||
} \
|
||||
\
|
||||
ATOMIC_INLINE type \
|
||||
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
atomic_memory_order_t mo) { \
|
||||
/* \
|
||||
* Because of FreeBSD, we care about gcc 4.2, which doesn't have\
|
||||
* an atomic exchange builtin. We fake it with a CAS loop. \
|
||||
@ -129,8 +131,9 @@ atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
|
||||
\
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type prev = __sync_val_compare_and_swap(&a->repr, *expected, \
|
||||
desired); \
|
||||
if (prev == *expected) { \
|
||||
@ -142,8 +145,9 @@ atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
|
||||
} \
|
||||
ATOMIC_INLINE bool \
|
||||
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
|
||||
type *expected, type desired, atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type *expected, type desired, \
|
||||
atomic_memory_order_t success_mo, \
|
||||
atomic_memory_order_t failure_mo) { \
|
||||
type prev = __sync_val_compare_and_swap(&a->repr, *expected, \
|
||||
desired); \
|
||||
if (prev == *expected) { \
|
||||
|
@ -8,7 +8,6 @@ extern atomic_b_t background_thread_enabled_state;
|
||||
extern size_t n_background_threads;
|
||||
extern size_t max_background_threads;
|
||||
extern background_thread_info_t *background_thread_info;
|
||||
extern bool can_enable_background_thread;
|
||||
|
||||
bool background_thread_create(tsd_t *tsd, unsigned arena_ind);
|
||||
bool background_threads_enable(tsd_t *tsd);
|
||||
|
@ -15,7 +15,12 @@ background_thread_enabled_set(tsdn_t *tsdn, bool state) {
|
||||
JEMALLOC_ALWAYS_INLINE background_thread_info_t *
|
||||
arena_background_thread_info_get(arena_t *arena) {
|
||||
unsigned arena_ind = arena_ind_get(arena);
|
||||
return &background_thread_info[arena_ind % ncpus];
|
||||
return &background_thread_info[arena_ind % max_background_threads];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE background_thread_info_t *
|
||||
background_thread_info_get(size_t ind) {
|
||||
return &background_thread_info[ind % max_background_threads];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE uint64_t
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#define BACKGROUND_THREAD_INDEFINITE_SLEEP UINT64_MAX
|
||||
#define MAX_BACKGROUND_THREAD_LIMIT MALLOCX_ARENA_LIMIT
|
||||
#define DEFAULT_NUM_BACKGROUND_THREAD 4
|
||||
|
||||
typedef enum {
|
||||
background_thread_stopped,
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/* Embedded at the beginning of every block of base-managed virtual memory. */
|
||||
struct base_block_s {
|
||||
@ -46,7 +46,7 @@ struct base_s {
|
||||
base_block_t *blocks;
|
||||
|
||||
/* Heap of extents that track unused trailing space within blocks. */
|
||||
extent_heap_t avail[NSIZES];
|
||||
extent_heap_t avail[SC_NSIZES];
|
||||
|
||||
/* Stats, only maintained if config_stats. */
|
||||
size_t allocated;
|
||||
|
@ -1,10 +1,12 @@
|
||||
#ifndef JEMALLOC_INTERNAL_BIN_H
|
||||
#define JEMALLOC_INTERNAL_BIN_H
|
||||
|
||||
#include "jemalloc/internal/bin_stats.h"
|
||||
#include "jemalloc/internal/bin_types.h"
|
||||
#include "jemalloc/internal/extent_types.h"
|
||||
#include "jemalloc/internal/extent_structs.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/bin_stats.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/*
|
||||
* A bin contains a set of extents that are currently being used for slab
|
||||
@ -41,6 +43,9 @@ struct bin_info_s {
|
||||
/* Total number of regions in a slab for this bin's size class. */
|
||||
uint32_t nregs;
|
||||
|
||||
/* Number of sharded bins in each arena for this size class. */
|
||||
uint32_t n_shards;
|
||||
|
||||
/*
|
||||
* Metadata used to manipulate bitmaps for slabs associated with this
|
||||
* bin.
|
||||
@ -48,8 +53,7 @@ struct bin_info_s {
|
||||
bitmap_info_t bitmap_info;
|
||||
};
|
||||
|
||||
extern const bin_info_t bin_infos[NBINS];
|
||||
|
||||
extern bin_info_t bin_infos[SC_NBINS];
|
||||
|
||||
typedef struct bin_s bin_t;
|
||||
struct bin_s {
|
||||
@ -78,6 +82,18 @@ struct bin_s {
|
||||
bin_stats_t stats;
|
||||
};
|
||||
|
||||
/* A set of sharded bins of the same size class. */
|
||||
typedef struct bins_s bins_t;
|
||||
struct bins_s {
|
||||
/* Sharded bins. Dynamically sized. */
|
||||
bin_t *bin_shards;
|
||||
};
|
||||
|
||||
void bin_shard_sizes_boot(unsigned bin_shards[SC_NBINS]);
|
||||
bool bin_update_shard_size(unsigned bin_shards[SC_NBINS], size_t start_size,
|
||||
size_t end_size, size_t nshards);
|
||||
void bin_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]);
|
||||
|
||||
/* Initializes a bin to empty. Returns true on error. */
|
||||
bool bin_init(bin_t *bin);
|
||||
|
||||
@ -90,7 +106,7 @@ void bin_postfork_child(tsdn_t *tsdn, bin_t *bin);
|
||||
static inline void
|
||||
bin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
malloc_mutex_prof_read(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
|
||||
malloc_mutex_prof_accum(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
|
||||
dst_bin_stats->nmalloc += bin->stats.nmalloc;
|
||||
dst_bin_stats->ndalloc += bin->stats.ndalloc;
|
||||
dst_bin_stats->nrequests += bin->stats.nrequests;
|
||||
@ -100,6 +116,7 @@ bin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {
|
||||
dst_bin_stats->nslabs += bin->stats.nslabs;
|
||||
dst_bin_stats->reslabs += bin->stats.reslabs;
|
||||
dst_bin_stats->curslabs += bin->stats.curslabs;
|
||||
dst_bin_stats->nonfull_slabs += bin->stats.nonfull_slabs;
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,9 @@ struct bin_stats_s {
|
||||
/* Current number of slabs in this bin. */
|
||||
size_t curslabs;
|
||||
|
||||
/* Current size of nonfull slabs heap in this bin. */
|
||||
size_t nonfull_slabs;
|
||||
|
||||
mutex_prof_data_t mutex_data;
|
||||
};
|
||||
|
||||
|
17
contrib/jemalloc/include/jemalloc/internal/bin_types.h
Normal file
17
contrib/jemalloc/include/jemalloc/internal/bin_types.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef JEMALLOC_INTERNAL_BIN_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_BIN_TYPES_H
|
||||
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
#define BIN_SHARDS_MAX (1 << EXTENT_BITS_BINSHARD_WIDTH)
|
||||
#define N_BIN_SHARDS_DEFAULT 1
|
||||
|
||||
/* Used in TSD static initializer only. Real init in arena_bind(). */
|
||||
#define TSD_BINSHARDS_ZERO_INITIALIZER {{UINT8_MAX}}
|
||||
|
||||
typedef struct tsd_binshards_s tsd_binshards_t;
|
||||
struct tsd_binshards_s {
|
||||
uint8_t binshard[SC_NBINS];
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BIN_TYPES_H */
|
@ -27,6 +27,25 @@ ffs_u(unsigned bitmap) {
|
||||
return JEMALLOC_INTERNAL_FFS(bitmap);
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_INTERNAL_POPCOUNTL
|
||||
BIT_UTIL_INLINE unsigned
|
||||
popcount_lu(unsigned long bitmap) {
|
||||
return JEMALLOC_INTERNAL_POPCOUNTL(bitmap);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Clears first unset bit in bitmap, and returns
|
||||
* place of bit. bitmap *must not* be 0.
|
||||
*/
|
||||
|
||||
BIT_UTIL_INLINE size_t
|
||||
cfs_lu(unsigned long* bitmap) {
|
||||
size_t bit = ffs_lu(*bitmap) - 1;
|
||||
*bitmap ^= ZU(1) << bit;
|
||||
return bit;
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
ffs_zu(size_t bitmap) {
|
||||
#if LG_SIZEOF_PTR == LG_SIZEOF_INT
|
||||
@ -63,6 +82,22 @@ ffs_u32(uint32_t bitmap) {
|
||||
|
||||
BIT_UTIL_INLINE uint64_t
|
||||
pow2_ceil_u64(uint64_t x) {
|
||||
#if (defined(__amd64__) || defined(__x86_64__) || defined(JEMALLOC_HAVE_BUILTIN_CLZ))
|
||||
if(unlikely(x <= 1)) {
|
||||
return x;
|
||||
}
|
||||
size_t msb_on_index;
|
||||
#if (defined(__amd64__) || defined(__x86_64__))
|
||||
asm ("bsrq %1, %0"
|
||||
: "=r"(msb_on_index) // Outputs.
|
||||
: "r"(x-1) // Inputs.
|
||||
);
|
||||
#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
|
||||
msb_on_index = (63 ^ __builtin_clzll(x - 1));
|
||||
#endif
|
||||
assert(msb_on_index < 63);
|
||||
return 1ULL << (msb_on_index + 1);
|
||||
#else
|
||||
x--;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
@ -72,10 +107,27 @@ pow2_ceil_u64(uint64_t x) {
|
||||
x |= x >> 32;
|
||||
x++;
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
BIT_UTIL_INLINE uint32_t
|
||||
pow2_ceil_u32(uint32_t x) {
|
||||
#if ((defined(__i386__) || defined(JEMALLOC_HAVE_BUILTIN_CLZ)) && (!defined(__s390__)))
|
||||
if(unlikely(x <= 1)) {
|
||||
return x;
|
||||
}
|
||||
size_t msb_on_index;
|
||||
#if (defined(__i386__))
|
||||
asm ("bsr %1, %0"
|
||||
: "=r"(msb_on_index) // Outputs.
|
||||
: "r"(x-1) // Inputs.
|
||||
);
|
||||
#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
|
||||
msb_on_index = (31 ^ __builtin_clz(x - 1));
|
||||
#endif
|
||||
assert(msb_on_index < 31);
|
||||
return 1U << (msb_on_index + 1);
|
||||
#else
|
||||
x--;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
@ -84,6 +136,7 @@ pow2_ceil_u32(uint32_t x) {
|
||||
x |= x >> 16;
|
||||
x++;
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compute the smallest power of 2 that is >= x. */
|
||||
@ -160,6 +213,27 @@ lg_floor(size_t x) {
|
||||
}
|
||||
#endif
|
||||
|
||||
BIT_UTIL_INLINE unsigned
|
||||
lg_ceil(size_t x) {
|
||||
return lg_floor(x) + ((x & (x - 1)) == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
#undef BIT_UTIL_INLINE
|
||||
|
||||
/* A compile-time version of lg_floor and lg_ceil. */
|
||||
#define LG_FLOOR_1(x) 0
|
||||
#define LG_FLOOR_2(x) (x < (1ULL << 1) ? LG_FLOOR_1(x) : 1 + LG_FLOOR_1(x >> 1))
|
||||
#define LG_FLOOR_4(x) (x < (1ULL << 2) ? LG_FLOOR_2(x) : 2 + LG_FLOOR_2(x >> 2))
|
||||
#define LG_FLOOR_8(x) (x < (1ULL << 4) ? LG_FLOOR_4(x) : 4 + LG_FLOOR_4(x >> 4))
|
||||
#define LG_FLOOR_16(x) (x < (1ULL << 8) ? LG_FLOOR_8(x) : 8 + LG_FLOOR_8(x >> 8))
|
||||
#define LG_FLOOR_32(x) (x < (1ULL << 16) ? LG_FLOOR_16(x) : 16 + LG_FLOOR_16(x >> 16))
|
||||
#define LG_FLOOR_64(x) (x < (1ULL << 32) ? LG_FLOOR_32(x) : 32 + LG_FLOOR_32(x >> 32))
|
||||
#if LG_SIZEOF_PTR == 2
|
||||
# define LG_FLOOR(x) LG_FLOOR_32((x))
|
||||
#else
|
||||
# define LG_FLOOR(x) LG_FLOOR_64((x))
|
||||
#endif
|
||||
|
||||
#define LG_CEIL(x) (LG_FLOOR(x) + (((x) & ((x) - 1)) == 0 ? 0 : 1))
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_BIT_UTIL_H */
|
||||
|
@ -3,18 +3,18 @@
|
||||
|
||||
#include "jemalloc/internal/arena_types.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
typedef unsigned long bitmap_t;
|
||||
#define LG_SIZEOF_BITMAP LG_SIZEOF_LONG
|
||||
|
||||
/* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */
|
||||
#if LG_SLAB_MAXREGS > LG_CEIL_NSIZES
|
||||
#if LG_SLAB_MAXREGS > LG_CEIL(SC_NSIZES)
|
||||
/* Maximum bitmap bit count is determined by maximum regions per slab. */
|
||||
# define LG_BITMAP_MAXBITS LG_SLAB_MAXREGS
|
||||
#else
|
||||
/* Maximum bitmap bit count is determined by number of extent size classes. */
|
||||
# define LG_BITMAP_MAXBITS LG_CEIL_NSIZES
|
||||
# define LG_BITMAP_MAXBITS LG_CEIL(SC_NSIZES)
|
||||
#endif
|
||||
#define BITMAP_MAXBITS (ZU(1) << LG_BITMAP_MAXBITS)
|
||||
|
||||
|
@ -88,11 +88,21 @@ JEMALLOC_ALWAYS_INLINE void *
|
||||
cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
|
||||
void *ret;
|
||||
|
||||
if (unlikely(bin->ncached == 0)) {
|
||||
bin->low_water = -1;
|
||||
*success = false;
|
||||
return NULL;
|
||||
bin->ncached--;
|
||||
|
||||
/*
|
||||
* Check for both bin->ncached == 0 and ncached < low_water
|
||||
* in a single branch.
|
||||
*/
|
||||
if (unlikely(bin->ncached <= bin->low_water)) {
|
||||
bin->low_water = bin->ncached;
|
||||
if (bin->ncached == -1) {
|
||||
bin->ncached = 0;
|
||||
*success = false;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* success (instead of ret) should be checked upon the return of this
|
||||
* function. We avoid checking (ret == NULL) because there is never a
|
||||
@ -101,14 +111,21 @@ cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
|
||||
* cacheline).
|
||||
*/
|
||||
*success = true;
|
||||
ret = *(bin->avail - bin->ncached);
|
||||
bin->ncached--;
|
||||
|
||||
if (unlikely(bin->ncached < bin->low_water)) {
|
||||
bin->low_water = bin->ncached;
|
||||
}
|
||||
ret = *(bin->avail - (bin->ncached + 1));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
cache_bin_dalloc_easy(cache_bin_t *bin, cache_bin_info_t *bin_info, void *ptr) {
|
||||
if (unlikely(bin->ncached == bin_info->ncached_max)) {
|
||||
return false;
|
||||
}
|
||||
assert(bin->ncached < bin_info->ncached_max);
|
||||
bin->ncached++;
|
||||
*(bin->avail - bin->ncached) = ptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_CACHE_BIN_H */
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "jemalloc/internal/malloc_io.h"
|
||||
#include "jemalloc/internal/mutex_prof.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/stats.h"
|
||||
|
||||
/* Maximum ctl tree depth. */
|
||||
@ -39,9 +39,12 @@ typedef struct ctl_arena_stats_s {
|
||||
uint64_t nmalloc_small;
|
||||
uint64_t ndalloc_small;
|
||||
uint64_t nrequests_small;
|
||||
uint64_t nfills_small;
|
||||
uint64_t nflushes_small;
|
||||
|
||||
bin_stats_t bstats[NBINS];
|
||||
arena_stats_large_t lstats[NSIZES - NBINS];
|
||||
bin_stats_t bstats[SC_NBINS];
|
||||
arena_stats_large_t lstats[SC_NSIZES - SC_NBINS];
|
||||
arena_stats_extents_t estats[SC_NPSIZES];
|
||||
} ctl_arena_stats_t;
|
||||
|
||||
typedef struct ctl_stats_s {
|
||||
|
@ -45,7 +45,9 @@ struct emitter_col_s {
|
||||
int int_val;
|
||||
unsigned unsigned_val;
|
||||
uint32_t uint32_val;
|
||||
uint32_t uint32_t_val;
|
||||
uint64_t uint64_val;
|
||||
uint64_t uint64_t_val;
|
||||
size_t size_val;
|
||||
ssize_t ssize_val;
|
||||
const char *str_val;
|
||||
@ -60,17 +62,6 @@ struct emitter_row_s {
|
||||
ql_head(emitter_col_t) cols;
|
||||
};
|
||||
|
||||
static inline void
|
||||
emitter_row_init(emitter_row_t *row) {
|
||||
ql_new(&row->cols);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
|
||||
ql_elm_new(col, link);
|
||||
ql_tail_insert(&row->cols, col, link);
|
||||
}
|
||||
|
||||
typedef struct emitter_s emitter_t;
|
||||
struct emitter_s {
|
||||
emitter_output_t output;
|
||||
@ -80,18 +71,10 @@ struct emitter_s {
|
||||
int nesting_depth;
|
||||
/* True if we've already emitted a value at the given depth. */
|
||||
bool item_at_depth;
|
||||
/* True if we emitted a key and will emit corresponding value next. */
|
||||
bool emitted_key;
|
||||
};
|
||||
|
||||
static inline void
|
||||
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
|
||||
void (*write_cb)(void *, const char *), void *cbopaque) {
|
||||
emitter->output = emitter_output;
|
||||
emitter->write_cb = write_cb;
|
||||
emitter->cbopaque = cbopaque;
|
||||
emitter->item_at_depth = false;
|
||||
emitter->nesting_depth = 0;
|
||||
}
|
||||
|
||||
/* Internal convenience function. Write to the emitter the given string. */
|
||||
JEMALLOC_FORMAT_PRINTF(2, 3)
|
||||
static inline void
|
||||
@ -103,22 +86,11 @@ emitter_printf(emitter_t *emitter, const char *format, ...) {
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Write to the emitter the given string, but only in table mode. */
|
||||
JEMALLOC_FORMAT_PRINTF(2, 3)
|
||||
static inline void
|
||||
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
static inline const char * JEMALLOC_FORMAT_ARG(3)
|
||||
emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
|
||||
emitter_justify_t justify, int width) {
|
||||
size_t written;
|
||||
fmt_specifier++;
|
||||
if (justify == emitter_justify_none) {
|
||||
written = malloc_snprintf(out_fmt, out_size,
|
||||
"%%%s", fmt_specifier);
|
||||
@ -131,6 +103,7 @@ emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
|
||||
}
|
||||
/* Only happens in case of bad format string, which *we* choose. */
|
||||
assert(written < out_size);
|
||||
return out_fmt;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -156,26 +129,27 @@ emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
#define EMIT_SIMPLE(type, format) \
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width); \
|
||||
emitter_printf(emitter, fmt, *(const type *)value); \
|
||||
emitter_printf(emitter, \
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width), \
|
||||
*(const type *)value);
|
||||
|
||||
switch (value_type) {
|
||||
case emitter_type_bool:
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
|
||||
emitter_printf(emitter, fmt, *(const bool *)value ?
|
||||
"true" : "false");
|
||||
emitter_printf(emitter,
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
|
||||
*(const bool *)value ? "true" : "false");
|
||||
break;
|
||||
case emitter_type_int:
|
||||
EMIT_SIMPLE(int, "d")
|
||||
EMIT_SIMPLE(int, "%d")
|
||||
break;
|
||||
case emitter_type_unsigned:
|
||||
EMIT_SIMPLE(unsigned, "u")
|
||||
EMIT_SIMPLE(unsigned, "%u")
|
||||
break;
|
||||
case emitter_type_ssize:
|
||||
EMIT_SIMPLE(ssize_t, "zd")
|
||||
EMIT_SIMPLE(ssize_t, "%zd")
|
||||
break;
|
||||
case emitter_type_size:
|
||||
EMIT_SIMPLE(size_t, "zu")
|
||||
EMIT_SIMPLE(size_t, "%zu")
|
||||
break;
|
||||
case emitter_type_string:
|
||||
str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
|
||||
@ -185,17 +159,17 @@ emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
|
||||
* anywhere near the fmt size.
|
||||
*/
|
||||
assert(str_written < BUF_SIZE);
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
|
||||
emitter_printf(emitter, fmt, buf);
|
||||
emitter_printf(emitter,
|
||||
emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width), buf);
|
||||
break;
|
||||
case emitter_type_uint32:
|
||||
EMIT_SIMPLE(uint32_t, FMTu32)
|
||||
EMIT_SIMPLE(uint32_t, "%" FMTu32)
|
||||
break;
|
||||
case emitter_type_uint64:
|
||||
EMIT_SIMPLE(uint64_t, FMTu64)
|
||||
EMIT_SIMPLE(uint64_t, "%" FMTu64)
|
||||
break;
|
||||
case emitter_type_title:
|
||||
EMIT_SIMPLE(char *const, "s");
|
||||
EMIT_SIMPLE(char *const, "%s");
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
@ -235,47 +209,143 @@ emitter_indent(emitter_t *emitter) {
|
||||
|
||||
static inline void
|
||||
emitter_json_key_prefix(emitter_t *emitter) {
|
||||
if (emitter->emitted_key) {
|
||||
emitter->emitted_key = false;
|
||||
return;
|
||||
}
|
||||
emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
|
||||
emitter_indent(emitter);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_begin(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth == 0);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
} else {
|
||||
// tabular init
|
||||
emitter_printf(emitter, "%s", "");
|
||||
}
|
||||
}
|
||||
/******************************************************************************/
|
||||
/* Public functions for emitter_t. */
|
||||
|
||||
static inline void
|
||||
emitter_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth == 1);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n}\n");
|
||||
}
|
||||
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
|
||||
void (*write_cb)(void *, const char *), void *cbopaque) {
|
||||
emitter->output = emitter_output;
|
||||
emitter->write_cb = write_cb;
|
||||
emitter->cbopaque = cbopaque;
|
||||
emitter->item_at_depth = false;
|
||||
emitter->emitted_key = false;
|
||||
emitter->nesting_depth = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note emits a different kv pair as well, but only in table mode. Omits the
|
||||
* note if table_note_key is NULL.
|
||||
/******************************************************************************/
|
||||
/* JSON public API. */
|
||||
|
||||
/*
|
||||
* Emits a key (e.g. as appears in an object). The next json entity emitted will
|
||||
* be the corresponding value.
|
||||
*/
|
||||
static inline void
|
||||
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_json_key(emitter_t *emitter, const char *json_key) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "\"%s\": ", json_key);
|
||||
emitter->emitted_key = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_value(emitter_t *emitter, emitter_type_t value_type,
|
||||
const void *value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
value_type, value);
|
||||
emitter->item_at_depth = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand for calling emitter_json_key and then emitter_json_value. */
|
||||
static inline void
|
||||
emitter_json_kv(emitter_t *emitter, const char *json_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_value(emitter, value_type, value);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_array_begin(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "[");
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand for calling emitter_json_key and then emitter_json_array_begin. */
|
||||
static inline void
|
||||
emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_array_begin(emitter);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_array_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "]");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_object_begin(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand for calling emitter_json_key and then emitter_json_object_begin. */
|
||||
static inline void
|
||||
emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_object_begin(emitter);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_object_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* Table public API. */
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "%s\n", table_key);
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_nest_dec(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_kv_note(emitter_t *emitter, const char *table_key,
|
||||
emitter_type_t value_type, const void *value,
|
||||
const char *table_note_key, emitter_type_t table_note_value_type,
|
||||
const void *table_note_value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "\"%s\": ", json_key);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
value_type, value);
|
||||
} else {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "%s: ", table_key);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
@ -292,130 +362,22 @@ emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_table_kv(emitter_t *emitter, const char *table_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
|
||||
emitter_table_kv_note(emitter, table_key, value_type, value, NULL,
|
||||
emitter_type_bool, NULL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_kv(emitter_t *emitter, const char *json_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_kv(emitter, json_key, NULL, value_type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write to the emitter the given string, but only in table mode. */
|
||||
JEMALLOC_FORMAT_PRINTF(2, 3)
|
||||
static inline void
|
||||
emitter_table_kv(emitter_t *emitter, const char *table_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_kv(emitter, NULL, table_key, value_type, value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_begin(emitter_t *emitter, const char *json_key,
|
||||
const char *table_header) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "\"%s\": {", json_key);
|
||||
emitter_nest_inc(emitter);
|
||||
} else {
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "%s\n", table_header);
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "}");
|
||||
} else {
|
||||
emitter_nest_dec(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_dict_begin(emitter_t *emitter, const char *json_key) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_dict_begin(emitter, json_key, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_dict_end(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_dict_begin(emitter, NULL, table_key);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_table_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_table) {
|
||||
emitter_dict_end(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_begin(emitter_t *emitter, const char *json_key) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "\"%s\": [", json_key);
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "]");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_obj_begin(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_obj_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth > 0);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n");
|
||||
emitter_indent(emitter);
|
||||
emitter_printf(emitter, "}");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,
|
||||
const void *value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key_prefix(emitter);
|
||||
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||
value_type, value);
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,4 +394,93 @@ emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
|
||||
emitter_table_printf(emitter, "\n");
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_row_init(emitter_row_t *row) {
|
||||
ql_new(&row->cols);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
|
||||
ql_elm_new(col, link);
|
||||
ql_tail_insert(&row->cols, col, link);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
* Generalized public API. Emits using either JSON or table, according to
|
||||
* settings in the emitter_t. */
|
||||
|
||||
/*
|
||||
* Note emits a different kv pair as well, but only in table mode. Omits the
|
||||
* note if table_note_key is NULL.
|
||||
*/
|
||||
static inline void
|
||||
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_type_t value_type, const void *value,
|
||||
const char *table_note_key, emitter_type_t table_note_value_type,
|
||||
const void *table_note_value) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_value(emitter, value_type, value);
|
||||
} else {
|
||||
emitter_table_kv_note(emitter, table_key, value_type, value,
|
||||
table_note_key, table_note_value_type, table_note_value);
|
||||
}
|
||||
emitter->item_at_depth = true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||
emitter_type_t value_type, const void *value) {
|
||||
emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
|
||||
emitter_type_bool, NULL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_begin(emitter_t *emitter, const char *json_key,
|
||||
const char *table_header) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_key(emitter, json_key);
|
||||
emitter_json_object_begin(emitter);
|
||||
} else {
|
||||
emitter_table_dict_begin(emitter, table_header);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_dict_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
emitter_json_object_end(emitter);
|
||||
} else {
|
||||
emitter_table_dict_end(emitter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_begin(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth == 0);
|
||||
emitter_printf(emitter, "{");
|
||||
emitter_nest_inc(emitter);
|
||||
} else {
|
||||
/*
|
||||
* This guarantees that we always call write_cb at least once.
|
||||
* This is useful if some invariant is established by each call
|
||||
* to write_cb, but doesn't hold initially: e.g., some buffer
|
||||
* holds a null-terminated string.
|
||||
*/
|
||||
emitter_printf(emitter, "%s", "");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
emitter_end(emitter_t *emitter) {
|
||||
if (emitter->output == emitter_output_json) {
|
||||
assert(emitter->nesting_depth == 1);
|
||||
emitter_nest_dec(emitter);
|
||||
emitter_printf(emitter, "\n}\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EMITTER_H */
|
||||
|
@ -24,13 +24,17 @@ size_t extent_size_quantize_floor(size_t size);
|
||||
size_t extent_size_quantize_ceil(size_t size);
|
||||
#endif
|
||||
|
||||
rb_proto(, extent_avail_, extent_tree_t, extent_t)
|
||||
ph_proto(, extent_avail_, extent_tree_t, extent_t)
|
||||
ph_proto(, extent_heap_, extent_heap_t, extent_t)
|
||||
|
||||
bool extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,
|
||||
bool delay_coalesce);
|
||||
extent_state_t extents_state_get(const extents_t *extents);
|
||||
size_t extents_npages_get(extents_t *extents);
|
||||
/* Get the number of extents in the given page size index. */
|
||||
size_t extents_nextents_get(extents_t *extents, pszind_t ind);
|
||||
/* Get the sum total bytes of the extents in the given page size index. */
|
||||
size_t extents_nbytes_get(extents_t *extents, pszind_t ind);
|
||||
extent_t *extents_alloc(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,
|
||||
size_t size, size_t pad, size_t alignment, bool slab, szind_t szind,
|
||||
@ -70,4 +74,10 @@ bool extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
|
||||
bool extent_boot(void);
|
||||
|
||||
void extent_util_stats_get(tsdn_t *tsdn, const void *ptr,
|
||||
size_t *nfree, size_t *nregs, size_t *size);
|
||||
void extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
|
||||
size_t *nfree, size_t *nregs, size_t *size,
|
||||
size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_EXTERNS_H */
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "jemalloc/internal/pages.h"
|
||||
#include "jemalloc/internal/prng.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
|
||||
static inline void
|
||||
@ -34,18 +35,19 @@ extent_unlock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {
|
||||
(uintptr_t)extent2);
|
||||
}
|
||||
|
||||
static inline arena_t *
|
||||
extent_arena_get(const extent_t *extent) {
|
||||
static inline unsigned
|
||||
extent_arena_ind_get(const extent_t *extent) {
|
||||
unsigned arena_ind = (unsigned)((extent->e_bits &
|
||||
EXTENT_BITS_ARENA_MASK) >> EXTENT_BITS_ARENA_SHIFT);
|
||||
/*
|
||||
* The following check is omitted because we should never actually read
|
||||
* a NULL arena pointer.
|
||||
*/
|
||||
if (false && arena_ind >= MALLOCX_ARENA_LIMIT) {
|
||||
return NULL;
|
||||
}
|
||||
assert(arena_ind < MALLOCX_ARENA_LIMIT);
|
||||
|
||||
return arena_ind;
|
||||
}
|
||||
|
||||
static inline arena_t *
|
||||
extent_arena_get(const extent_t *extent) {
|
||||
unsigned arena_ind = extent_arena_ind_get(extent);
|
||||
|
||||
return (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
@ -53,14 +55,14 @@ static inline szind_t
|
||||
extent_szind_get_maybe_invalid(const extent_t *extent) {
|
||||
szind_t szind = (szind_t)((extent->e_bits & EXTENT_BITS_SZIND_MASK) >>
|
||||
EXTENT_BITS_SZIND_SHIFT);
|
||||
assert(szind <= NSIZES);
|
||||
assert(szind <= SC_NSIZES);
|
||||
return szind;
|
||||
}
|
||||
|
||||
static inline szind_t
|
||||
extent_szind_get(const extent_t *extent) {
|
||||
szind_t szind = extent_szind_get_maybe_invalid(extent);
|
||||
assert(szind < NSIZES); /* Never call when "invalid". */
|
||||
assert(szind < SC_NSIZES); /* Never call when "invalid". */
|
||||
return szind;
|
||||
}
|
||||
|
||||
@ -69,6 +71,14 @@ extent_usize_get(const extent_t *extent) {
|
||||
return sz_index2size(extent_szind_get(extent));
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
extent_binshard_get(const extent_t *extent) {
|
||||
unsigned binshard = (unsigned)((extent->e_bits &
|
||||
EXTENT_BITS_BINSHARD_MASK) >> EXTENT_BITS_BINSHARD_SHIFT);
|
||||
assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
|
||||
return binshard;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
extent_sn_get(const extent_t *extent) {
|
||||
return (size_t)((extent->e_bits & EXTENT_BITS_SN_MASK) >>
|
||||
@ -176,6 +186,11 @@ extent_prof_tctx_get(const extent_t *extent) {
|
||||
ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline nstime_t
|
||||
extent_prof_alloc_time_get(const extent_t *extent) {
|
||||
return extent->e_alloc_time;
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_arena_set(extent_t *extent, arena_t *arena) {
|
||||
unsigned arena_ind = (arena != NULL) ? arena_ind_get(arena) : ((1U <<
|
||||
@ -184,13 +199,21 @@ extent_arena_set(extent_t *extent, arena_t *arena) {
|
||||
((uint64_t)arena_ind << EXTENT_BITS_ARENA_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_binshard_set(extent_t *extent, unsigned binshard) {
|
||||
/* The assertion assumes szind is set already. */
|
||||
assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_BINSHARD_MASK) |
|
||||
((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_addr_set(extent_t *extent, void *addr) {
|
||||
extent->e_addr = addr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_addr_randomize(UNUSED tsdn_t *tsdn, extent_t *extent, size_t alignment) {
|
||||
extent_addr_randomize(tsdn_t *tsdn, extent_t *extent, size_t alignment) {
|
||||
assert(extent_base_get(extent) == extent_addr_get(extent));
|
||||
|
||||
if (alignment < PAGE) {
|
||||
@ -234,7 +257,7 @@ extent_bsize_set(extent_t *extent, size_t bsize) {
|
||||
|
||||
static inline void
|
||||
extent_szind_set(extent_t *extent, szind_t szind) {
|
||||
assert(szind <= NSIZES); /* NSIZES means "invalid". */
|
||||
assert(szind <= SC_NSIZES); /* SC_NSIZES means "invalid". */
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SZIND_MASK) |
|
||||
((uint64_t)szind << EXTENT_BITS_SZIND_SHIFT);
|
||||
}
|
||||
@ -246,6 +269,16 @@ extent_nfree_set(extent_t *extent, unsigned nfree) {
|
||||
((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_binshard_set(extent_t *extent, unsigned nfree, unsigned binshard) {
|
||||
/* The assertion assumes szind is set already. */
|
||||
assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
|
||||
extent->e_bits = (extent->e_bits &
|
||||
(~EXTENT_BITS_NFREE_MASK & ~EXTENT_BITS_BINSHARD_MASK)) |
|
||||
((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT) |
|
||||
((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_inc(extent_t *extent) {
|
||||
assert(extent_slab_get(extent));
|
||||
@ -258,6 +291,12 @@ extent_nfree_dec(extent_t *extent) {
|
||||
extent->e_bits -= ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_sub(extent_t *extent, uint64_t n) {
|
||||
assert(extent_slab_get(extent));
|
||||
extent->e_bits -= (n << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_sn_set(extent_t *extent, size_t sn) {
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SN_MASK) |
|
||||
@ -299,10 +338,35 @@ extent_prof_tctx_set(extent_t *extent, prof_tctx_t *tctx) {
|
||||
atomic_store_p(&extent->e_prof_tctx, tctx, ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_prof_alloc_time_set(extent_t *extent, nstime_t t) {
|
||||
nstime_copy(&extent->e_alloc_time, &t);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
extent_is_head_get(extent_t *extent) {
|
||||
if (maps_coalesce) {
|
||||
not_reached();
|
||||
}
|
||||
|
||||
return (bool)((extent->e_bits & EXTENT_BITS_IS_HEAD_MASK) >>
|
||||
EXTENT_BITS_IS_HEAD_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_is_head_set(extent_t *extent, bool is_head) {
|
||||
if (maps_coalesce) {
|
||||
not_reached();
|
||||
}
|
||||
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_IS_HEAD_MASK) |
|
||||
((uint64_t)is_head << EXTENT_BITS_IS_HEAD_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_init(extent_t *extent, arena_t *arena, void *addr, size_t size,
|
||||
bool slab, szind_t szind, size_t sn, extent_state_t state, bool zeroed,
|
||||
bool committed, bool dumpable) {
|
||||
bool committed, bool dumpable, extent_head_state_t is_head) {
|
||||
assert(addr == PAGE_ADDR2BASE(addr) || !slab);
|
||||
|
||||
extent_arena_set(extent, arena);
|
||||
@ -316,6 +380,10 @@ extent_init(extent_t *extent, arena_t *arena, void *addr, size_t size,
|
||||
extent_committed_set(extent, committed);
|
||||
extent_dumpable_set(extent, dumpable);
|
||||
ql_elm_new(extent, ql_link);
|
||||
if (!maps_coalesce) {
|
||||
extent_is_head_set(extent, (is_head == EXTENT_IS_HEAD) ? true :
|
||||
false);
|
||||
}
|
||||
if (config_prof) {
|
||||
extent_prof_tctx_set(extent, NULL);
|
||||
}
|
||||
@ -327,7 +395,7 @@ extent_binit(extent_t *extent, void *addr, size_t bsize, size_t sn) {
|
||||
extent_addr_set(extent, addr);
|
||||
extent_bsize_set(extent, bsize);
|
||||
extent_slab_set(extent, false);
|
||||
extent_szind_set(extent, NSIZES);
|
||||
extent_szind_set(extent, SC_NSIZES);
|
||||
extent_sn_set(extent, sn);
|
||||
extent_state_set(extent, extent_state_active);
|
||||
extent_zeroed_set(extent, true);
|
||||
|
@ -2,11 +2,12 @@
|
||||
#define JEMALLOC_INTERNAL_EXTENT_STRUCTS_H
|
||||
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/ph.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
typedef enum {
|
||||
extent_state_active = 0,
|
||||
@ -28,9 +29,10 @@ struct extent_s {
|
||||
* t: state
|
||||
* i: szind
|
||||
* f: nfree
|
||||
* s: bin_shard
|
||||
* n: sn
|
||||
*
|
||||
* nnnnnnnn ... nnnnffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa
|
||||
* nnnnnnnn ... nnnnnnss ssssffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa
|
||||
*
|
||||
* arena_ind: Arena from which this extent came, or all 1 bits if
|
||||
* unassociated.
|
||||
@ -75,6 +77,8 @@ struct extent_s {
|
||||
*
|
||||
* nfree: Number of free regions in slab.
|
||||
*
|
||||
* bin_shard: the shard of the bin from which this extent came.
|
||||
*
|
||||
* sn: Serial number (potentially non-unique).
|
||||
*
|
||||
* Serial numbers may wrap around if !opt_retain, but as long as
|
||||
@ -112,7 +116,7 @@ struct extent_s {
|
||||
#define EXTENT_BITS_STATE_SHIFT (EXTENT_BITS_ZEROED_WIDTH + EXTENT_BITS_ZEROED_SHIFT)
|
||||
#define EXTENT_BITS_STATE_MASK MASK(EXTENT_BITS_STATE_WIDTH, EXTENT_BITS_STATE_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_SZIND_WIDTH LG_CEIL_NSIZES
|
||||
#define EXTENT_BITS_SZIND_WIDTH LG_CEIL(SC_NSIZES)
|
||||
#define EXTENT_BITS_SZIND_SHIFT (EXTENT_BITS_STATE_WIDTH + EXTENT_BITS_STATE_SHIFT)
|
||||
#define EXTENT_BITS_SZIND_MASK MASK(EXTENT_BITS_SZIND_WIDTH, EXTENT_BITS_SZIND_SHIFT)
|
||||
|
||||
@ -120,7 +124,15 @@ struct extent_s {
|
||||
#define EXTENT_BITS_NFREE_SHIFT (EXTENT_BITS_SZIND_WIDTH + EXTENT_BITS_SZIND_SHIFT)
|
||||
#define EXTENT_BITS_NFREE_MASK MASK(EXTENT_BITS_NFREE_WIDTH, EXTENT_BITS_NFREE_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_SN_SHIFT (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)
|
||||
#define EXTENT_BITS_BINSHARD_WIDTH 6
|
||||
#define EXTENT_BITS_BINSHARD_SHIFT (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)
|
||||
#define EXTENT_BITS_BINSHARD_MASK MASK(EXTENT_BITS_BINSHARD_WIDTH, EXTENT_BITS_BINSHARD_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_IS_HEAD_WIDTH 1
|
||||
#define EXTENT_BITS_IS_HEAD_SHIFT (EXTENT_BITS_BINSHARD_WIDTH + EXTENT_BITS_BINSHARD_SHIFT)
|
||||
#define EXTENT_BITS_IS_HEAD_MASK MASK(EXTENT_BITS_IS_HEAD_WIDTH, EXTENT_BITS_IS_HEAD_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_SN_SHIFT (EXTENT_BITS_IS_HEAD_WIDTH + EXTENT_BITS_IS_HEAD_SHIFT)
|
||||
#define EXTENT_BITS_SN_MASK (UINT64_MAX << EXTENT_BITS_SN_SHIFT)
|
||||
|
||||
/* Pointer to the extent that this structure is responsible for. */
|
||||
@ -160,11 +172,13 @@ struct extent_s {
|
||||
/* Small region slab metadata. */
|
||||
arena_slab_data_t e_slab_data;
|
||||
|
||||
/*
|
||||
* Profile counters, used for large objects. Points to a
|
||||
* prof_tctx_t.
|
||||
*/
|
||||
atomic_p_t e_prof_tctx;
|
||||
/* Profiling data, used for large objects. */
|
||||
struct {
|
||||
/* Time when this was allocated. */
|
||||
nstime_t e_alloc_time;
|
||||
/* Points to a prof_tctx_t. */
|
||||
atomic_p_t e_prof_tctx;
|
||||
};
|
||||
};
|
||||
};
|
||||
typedef ql_head(extent_t) extent_list_t;
|
||||
@ -180,14 +194,16 @@ struct extents_s {
|
||||
*
|
||||
* Synchronization: mtx.
|
||||
*/
|
||||
extent_heap_t heaps[NPSIZES+1];
|
||||
extent_heap_t heaps[SC_NPSIZES + 1];
|
||||
atomic_zu_t nextents[SC_NPSIZES + 1];
|
||||
atomic_zu_t nbytes[SC_NPSIZES + 1];
|
||||
|
||||
/*
|
||||
* Bitmap for which set bits correspond to non-empty heaps.
|
||||
*
|
||||
* Synchronization: mtx.
|
||||
*/
|
||||
bitmap_t bitmap[BITMAP_GROUPS(NPSIZES+1)];
|
||||
bitmap_t bitmap[BITMAP_GROUPS(SC_NPSIZES + 1)];
|
||||
|
||||
/*
|
||||
* LRU of all extents in heaps.
|
||||
@ -216,4 +232,25 @@ struct extents_s {
|
||||
bool delay_coalesce;
|
||||
};
|
||||
|
||||
/*
|
||||
* The following two structs are for experimental purposes. See
|
||||
* experimental_utilization_query_ctl and
|
||||
* experimental_utilization_batch_query_ctl in src/ctl.c.
|
||||
*/
|
||||
|
||||
struct extent_util_stats_s {
|
||||
size_t nfree;
|
||||
size_t nregs;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct extent_util_stats_verbose_s {
|
||||
void *slabcur_addr;
|
||||
size_t nfree;
|
||||
size_t nregs;
|
||||
size_t size;
|
||||
size_t bin_nfree;
|
||||
size_t bin_nregs;
|
||||
};
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_STRUCTS_H */
|
||||
|
@ -4,9 +4,10 @@
|
||||
typedef struct extent_s extent_t;
|
||||
typedef struct extents_s extents_t;
|
||||
|
||||
#define EXTENT_HOOKS_INITIALIZER NULL
|
||||
typedef struct extent_util_stats_s extent_util_stats_t;
|
||||
typedef struct extent_util_stats_verbose_s extent_util_stats_verbose_t;
|
||||
|
||||
#define EXTENT_GROW_MAX_PIND (NPSIZES - 1)
|
||||
#define EXTENT_HOOKS_INITIALIZER NULL
|
||||
|
||||
/*
|
||||
* When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)
|
||||
@ -14,4 +15,9 @@ typedef struct extents_s extents_t;
|
||||
*/
|
||||
#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6
|
||||
|
||||
typedef enum {
|
||||
EXTENT_NOT_HEAD,
|
||||
EXTENT_IS_HEAD /* Only relevant for Windows && opt.retain. */
|
||||
} extent_head_state_t;
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTENT_TYPES_H */
|
||||
|
@ -104,8 +104,8 @@ hash_x86_32(const void *key, int len, uint32_t seed) {
|
||||
uint32_t k1 = 0;
|
||||
|
||||
switch (len & 3) {
|
||||
case 3: k1 ^= tail[2] << 16;
|
||||
case 2: k1 ^= tail[1] << 8;
|
||||
case 3: k1 ^= tail[2] << 16; JEMALLOC_FALLTHROUGH
|
||||
case 2: k1 ^= tail[1] << 8; JEMALLOC_FALLTHROUGH
|
||||
case 1: k1 ^= tail[0]; k1 *= c1; k1 = hash_rotl_32(k1, 15);
|
||||
k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
@ -119,7 +119,7 @@ hash_x86_32(const void *key, int len, uint32_t seed) {
|
||||
return h1;
|
||||
}
|
||||
|
||||
UNUSED static inline void
|
||||
static inline void
|
||||
hash_x86_128(const void *key, const int len, uint32_t seed,
|
||||
uint64_t r_out[2]) {
|
||||
const uint8_t * data = (const uint8_t *) key;
|
||||
@ -177,28 +177,29 @@ hash_x86_128(const void *key, const int len, uint32_t seed,
|
||||
uint32_t k4 = 0;
|
||||
|
||||
switch (len & 15) {
|
||||
case 15: k4 ^= tail[14] << 16;
|
||||
case 14: k4 ^= tail[13] << 8;
|
||||
case 15: k4 ^= tail[14] << 16; JEMALLOC_FALLTHROUGH
|
||||
case 14: k4 ^= tail[13] << 8; JEMALLOC_FALLTHROUGH
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
case 12: k3 ^= tail[11] << 24;
|
||||
case 11: k3 ^= tail[10] << 16;
|
||||
case 10: k3 ^= tail[ 9] << 8;
|
||||
JEMALLOC_FALLTHROUGH
|
||||
case 12: k3 ^= tail[11] << 24; JEMALLOC_FALLTHROUGH
|
||||
case 11: k3 ^= tail[10] << 16; JEMALLOC_FALLTHROUGH
|
||||
case 10: k3 ^= tail[ 9] << 8; JEMALLOC_FALLTHROUGH
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
case 8: k2 ^= tail[ 7] << 24;
|
||||
case 7: k2 ^= tail[ 6] << 16;
|
||||
case 6: k2 ^= tail[ 5] << 8;
|
||||
JEMALLOC_FALLTHROUGH
|
||||
case 8: k2 ^= tail[ 7] << 24; JEMALLOC_FALLTHROUGH
|
||||
case 7: k2 ^= tail[ 6] << 16; JEMALLOC_FALLTHROUGH
|
||||
case 6: k2 ^= tail[ 5] << 8; JEMALLOC_FALLTHROUGH
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
case 4: k1 ^= tail[ 3] << 24;
|
||||
case 3: k1 ^= tail[ 2] << 16;
|
||||
case 2: k1 ^= tail[ 1] << 8;
|
||||
JEMALLOC_FALLTHROUGH
|
||||
case 4: k1 ^= tail[ 3] << 24; JEMALLOC_FALLTHROUGH
|
||||
case 3: k1 ^= tail[ 2] << 16; JEMALLOC_FALLTHROUGH
|
||||
case 2: k1 ^= tail[ 1] << 8; JEMALLOC_FALLTHROUGH
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +221,7 @@ hash_x86_128(const void *key, const int len, uint32_t seed,
|
||||
r_out[1] = (((uint64_t) h4) << 32) | h3;
|
||||
}
|
||||
|
||||
UNUSED static inline void
|
||||
static inline void
|
||||
hash_x64_128(const void *key, const int len, const uint32_t seed,
|
||||
uint64_t r_out[2]) {
|
||||
const uint8_t *data = (const uint8_t *) key;
|
||||
@ -260,22 +261,22 @@ hash_x64_128(const void *key, const int len, const uint32_t seed,
|
||||
uint64_t k2 = 0;
|
||||
|
||||
switch (len & 15) {
|
||||
case 15: k2 ^= ((uint64_t)(tail[14])) << 48; /* falls through */
|
||||
case 14: k2 ^= ((uint64_t)(tail[13])) << 40; /* falls through */
|
||||
case 13: k2 ^= ((uint64_t)(tail[12])) << 32; /* falls through */
|
||||
case 12: k2 ^= ((uint64_t)(tail[11])) << 24; /* falls through */
|
||||
case 11: k2 ^= ((uint64_t)(tail[10])) << 16; /* falls through */
|
||||
case 10: k2 ^= ((uint64_t)(tail[ 9])) << 8; /* falls through */
|
||||
case 15: k2 ^= ((uint64_t)(tail[14])) << 48; JEMALLOC_FALLTHROUGH
|
||||
case 14: k2 ^= ((uint64_t)(tail[13])) << 40; JEMALLOC_FALLTHROUGH
|
||||
case 13: k2 ^= ((uint64_t)(tail[12])) << 32; JEMALLOC_FALLTHROUGH
|
||||
case 12: k2 ^= ((uint64_t)(tail[11])) << 24; JEMALLOC_FALLTHROUGH
|
||||
case 11: k2 ^= ((uint64_t)(tail[10])) << 16; JEMALLOC_FALLTHROUGH
|
||||
case 10: k2 ^= ((uint64_t)(tail[ 9])) << 8; JEMALLOC_FALLTHROUGH
|
||||
case 9: k2 ^= ((uint64_t)(tail[ 8])) << 0;
|
||||
k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
|
||||
/* falls through */
|
||||
case 8: k1 ^= ((uint64_t)(tail[ 7])) << 56; /* falls through */
|
||||
case 7: k1 ^= ((uint64_t)(tail[ 6])) << 48; /* falls through */
|
||||
case 6: k1 ^= ((uint64_t)(tail[ 5])) << 40; /* falls through */
|
||||
case 5: k1 ^= ((uint64_t)(tail[ 4])) << 32; /* falls through */
|
||||
case 4: k1 ^= ((uint64_t)(tail[ 3])) << 24; /* falls through */
|
||||
case 3: k1 ^= ((uint64_t)(tail[ 2])) << 16; /* falls through */
|
||||
case 2: k1 ^= ((uint64_t)(tail[ 1])) << 8; /* falls through */
|
||||
JEMALLOC_FALLTHROUGH
|
||||
case 8: k1 ^= ((uint64_t)(tail[ 7])) << 56; JEMALLOC_FALLTHROUGH
|
||||
case 7: k1 ^= ((uint64_t)(tail[ 6])) << 48; JEMALLOC_FALLTHROUGH
|
||||
case 6: k1 ^= ((uint64_t)(tail[ 5])) << 40; JEMALLOC_FALLTHROUGH
|
||||
case 5: k1 ^= ((uint64_t)(tail[ 4])) << 32; JEMALLOC_FALLTHROUGH
|
||||
case 4: k1 ^= ((uint64_t)(tail[ 3])) << 24; JEMALLOC_FALLTHROUGH
|
||||
case 3: k1 ^= ((uint64_t)(tail[ 2])) << 16; JEMALLOC_FALLTHROUGH
|
||||
case 2: k1 ^= ((uint64_t)(tail[ 1])) << 8; JEMALLOC_FALLTHROUGH
|
||||
case 1: k1 ^= ((uint64_t)(tail[ 0])) << 0;
|
||||
k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
163
contrib/jemalloc/include/jemalloc/internal/hook.h
Normal file
163
contrib/jemalloc/include/jemalloc/internal/hook.h
Normal file
@ -0,0 +1,163 @@
|
||||
#ifndef JEMALLOC_INTERNAL_HOOK_H
|
||||
#define JEMALLOC_INTERNAL_HOOK_H
|
||||
|
||||
#include "jemalloc/internal/tsd.h"
|
||||
|
||||
/*
|
||||
* This API is *extremely* experimental, and may get ripped out, changed in API-
|
||||
* and ABI-incompatible ways, be insufficiently or incorrectly documented, etc.
|
||||
*
|
||||
* It allows hooking the stateful parts of the API to see changes as they
|
||||
* happen.
|
||||
*
|
||||
* Allocation hooks are called after the allocation is done, free hooks are
|
||||
* called before the free is done, and expand hooks are called after the
|
||||
* allocation is expanded.
|
||||
*
|
||||
* For realloc and rallocx, if the expansion happens in place, the expansion
|
||||
* hook is called. If it is moved, then the alloc hook is called on the new
|
||||
* location, and then the free hook is called on the old location (i.e. both
|
||||
* hooks are invoked in between the alloc and the dalloc).
|
||||
*
|
||||
* If we return NULL from OOM, then usize might not be trustworthy. Calling
|
||||
* realloc(NULL, size) only calls the alloc hook, and calling realloc(ptr, 0)
|
||||
* only calls the free hook. (Calling realloc(NULL, 0) is treated as malloc(0),
|
||||
* and only calls the alloc hook).
|
||||
*
|
||||
* Reentrancy:
|
||||
* Reentrancy is guarded against from within the hook implementation. If you
|
||||
* call allocator functions from within a hook, the hooks will not be invoked
|
||||
* again.
|
||||
* Threading:
|
||||
* The installation of a hook synchronizes with all its uses. If you can
|
||||
* prove the installation of a hook happens-before a jemalloc entry point,
|
||||
* then the hook will get invoked (unless there's a racing removal).
|
||||
*
|
||||
* Hook insertion appears to be atomic at a per-thread level (i.e. if a thread
|
||||
* allocates and has the alloc hook invoked, then a subsequent free on the
|
||||
* same thread will also have the free hook invoked).
|
||||
*
|
||||
* The *removal* of a hook does *not* block until all threads are done with
|
||||
* the hook. Hook authors have to be resilient to this, and need some
|
||||
* out-of-band mechanism for cleaning up any dynamically allocated memory
|
||||
* associated with their hook.
|
||||
* Ordering:
|
||||
* Order of hook execution is unspecified, and may be different than insertion
|
||||
* order.
|
||||
*/
|
||||
|
||||
#define HOOK_MAX 4
|
||||
|
||||
enum hook_alloc_e {
|
||||
hook_alloc_malloc,
|
||||
hook_alloc_posix_memalign,
|
||||
hook_alloc_aligned_alloc,
|
||||
hook_alloc_calloc,
|
||||
hook_alloc_memalign,
|
||||
hook_alloc_valloc,
|
||||
hook_alloc_mallocx,
|
||||
|
||||
/* The reallocating functions have both alloc and dalloc variants */
|
||||
hook_alloc_realloc,
|
||||
hook_alloc_rallocx,
|
||||
};
|
||||
/*
|
||||
* We put the enum typedef after the enum, since this file may get included by
|
||||
* jemalloc_cpp.cpp, and C++ disallows enum forward declarations.
|
||||
*/
|
||||
typedef enum hook_alloc_e hook_alloc_t;
|
||||
|
||||
enum hook_dalloc_e {
|
||||
hook_dalloc_free,
|
||||
hook_dalloc_dallocx,
|
||||
hook_dalloc_sdallocx,
|
||||
|
||||
/*
|
||||
* The dalloc halves of reallocation (not called if in-place expansion
|
||||
* happens).
|
||||
*/
|
||||
hook_dalloc_realloc,
|
||||
hook_dalloc_rallocx,
|
||||
};
|
||||
typedef enum hook_dalloc_e hook_dalloc_t;
|
||||
|
||||
|
||||
enum hook_expand_e {
|
||||
hook_expand_realloc,
|
||||
hook_expand_rallocx,
|
||||
hook_expand_xallocx,
|
||||
};
|
||||
typedef enum hook_expand_e hook_expand_t;
|
||||
|
||||
typedef void (*hook_alloc)(
|
||||
void *extra, hook_alloc_t type, void *result, uintptr_t result_raw,
|
||||
uintptr_t args_raw[3]);
|
||||
|
||||
typedef void (*hook_dalloc)(
|
||||
void *extra, hook_dalloc_t type, void *address, uintptr_t args_raw[3]);
|
||||
|
||||
typedef void (*hook_expand)(
|
||||
void *extra, hook_expand_t type, void *address, size_t old_usize,
|
||||
size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
|
||||
|
||||
typedef struct hooks_s hooks_t;
|
||||
struct hooks_s {
|
||||
hook_alloc alloc_hook;
|
||||
hook_dalloc dalloc_hook;
|
||||
hook_expand expand_hook;
|
||||
void *extra;
|
||||
};
|
||||
|
||||
/*
|
||||
* Begin implementation details; everything above this point might one day live
|
||||
* in a public API. Everything below this point never will.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The realloc pathways haven't gotten any refactoring love in a while, and it's
|
||||
* fairly difficult to pass information from the entry point to the hooks. We
|
||||
* put the informaiton the hooks will need into a struct to encapsulate
|
||||
* everything.
|
||||
*
|
||||
* Much of these pathways are force-inlined, so that the compiler can avoid
|
||||
* materializing this struct until we hit an extern arena function. For fairly
|
||||
* goofy reasons, *many* of the realloc paths hit an extern arena function.
|
||||
* These paths are cold enough that it doesn't matter; eventually, we should
|
||||
* rewrite the realloc code to make the expand-in-place and the
|
||||
* free-then-realloc paths more orthogonal, at which point we don't need to
|
||||
* spread the hook logic all over the place.
|
||||
*/
|
||||
typedef struct hook_ralloc_args_s hook_ralloc_args_t;
|
||||
struct hook_ralloc_args_s {
|
||||
/* I.e. as opposed to rallocx. */
|
||||
bool is_realloc;
|
||||
/*
|
||||
* The expand hook takes 4 arguments, even if only 3 are actually used;
|
||||
* we add an extra one in case the user decides to memcpy without
|
||||
* looking too closely at the hooked function.
|
||||
*/
|
||||
uintptr_t args[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns an opaque handle to be used when removing the hook. NULL means that
|
||||
* we couldn't install the hook.
|
||||
*/
|
||||
bool hook_boot();
|
||||
|
||||
void *hook_install(tsdn_t *tsdn, hooks_t *hooks);
|
||||
/* Uninstalls the hook with the handle previously returned from hook_install. */
|
||||
void hook_remove(tsdn_t *tsdn, void *opaque);
|
||||
|
||||
/* Hooks */
|
||||
|
||||
void hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
|
||||
uintptr_t args_raw[3]);
|
||||
|
||||
void hook_invoke_dalloc(hook_dalloc_t type, void *address,
|
||||
uintptr_t args_raw[3]);
|
||||
|
||||
void hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
|
||||
size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HOOK_H */
|
@ -1,12 +0,0 @@
|
||||
#ifndef JEMALLOC_INTERNAL_HOOKS_H
|
||||
#define JEMALLOC_INTERNAL_HOOKS_H
|
||||
|
||||
extern JEMALLOC_EXPORT void (*hooks_arena_new_hook)();
|
||||
extern JEMALLOC_EXPORT void (*hooks_libc_hook)();
|
||||
|
||||
#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
|
||||
|
||||
/* Note that this is undef'd and re-define'd in src/prof.c. */
|
||||
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_HOOKS_H */
|
@ -34,6 +34,9 @@
|
||||
# include <sys/uio.h>
|
||||
# endif
|
||||
# include <pthread.h>
|
||||
# ifdef __FreeBSD__
|
||||
# include <pthread_np.h>
|
||||
# endif
|
||||
# include <signal.h>
|
||||
# ifdef JEMALLOC_OS_UNFAIR_LOCK
|
||||
# include <os/lock.h>
|
||||
|
@ -45,29 +45,17 @@
|
||||
#define LG_VADDR 48
|
||||
|
||||
/* Defined if C11 atomics are available. */
|
||||
/* #undef JEMALLOC_C11_ATOMICS */
|
||||
#define JEMALLOC_C11_ATOMICS 1
|
||||
|
||||
/* Defined if GCC __atomic atomics are available. */
|
||||
/* #undef JEMALLOC_GCC_ATOMIC_ATOMICS */
|
||||
#define JEMALLOC_GCC_ATOMIC_ATOMICS 1
|
||||
/* and the 8-bit variant support. */
|
||||
#define JEMALLOC_GCC_U8_ATOMIC_ATOMICS 1
|
||||
|
||||
/* Defined if GCC __sync atomics are available. */
|
||||
#define JEMALLOC_GCC_SYNC_ATOMICS 1
|
||||
|
||||
/*
|
||||
* Defined if __sync_add_and_fetch(uint32_t *, uint32_t) and
|
||||
* __sync_sub_and_fetch(uint32_t *, uint32_t) are available, despite
|
||||
* __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 not being defined (which means the
|
||||
* functions are defined in libgcc instead of being inlines).
|
||||
*/
|
||||
#define JE_FORCE_SYNC_COMPARE_AND_SWAP_4
|
||||
|
||||
/*
|
||||
* Defined if __sync_add_and_fetch(uint64_t *, uint64_t) and
|
||||
* __sync_sub_and_fetch(uint64_t *, uint64_t) are available, despite
|
||||
* __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 not being defined (which means the
|
||||
* functions are defined in libgcc instead of being inlines).
|
||||
*/
|
||||
#define JE_FORCE_SYNC_COMPARE_AND_SWAP_8
|
||||
/* and the 8-bit variant support. */
|
||||
#define JEMALLOC_GCC_U8_SYNC_ATOMICS 1
|
||||
|
||||
/*
|
||||
* Defined if __builtin_clz() and __builtin_clzl() are available.
|
||||
@ -79,12 +67,6 @@
|
||||
*/
|
||||
/* #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 usable. */
|
||||
#define JEMALLOC_USE_SYSCALL
|
||||
|
||||
@ -154,6 +136,9 @@
|
||||
/* JEMALLOC_STATS enables statistics calculation. */
|
||||
#define JEMALLOC_STATS
|
||||
|
||||
/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
|
||||
/* #undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API */
|
||||
|
||||
/* JEMALLOC_PROF enables allocation profiling. */
|
||||
/* #undef JEMALLOC_PROF */
|
||||
|
||||
@ -224,7 +209,7 @@
|
||||
* Used to mark unreachable code to quiet "end of non-void" compiler warnings.
|
||||
* Don't use this directly; instead use unreachable() from util.h
|
||||
*/
|
||||
#define JEMALLOC_INTERNAL_UNREACHABLE abort
|
||||
#define JEMALLOC_INTERNAL_UNREACHABLE __builtin_unreachable
|
||||
|
||||
/*
|
||||
* ffs*() functions to use for bitmapping. Don't use these directly; instead,
|
||||
@ -234,6 +219,12 @@
|
||||
#define JEMALLOC_INTERNAL_FFSL __builtin_ffsl
|
||||
#define JEMALLOC_INTERNAL_FFS __builtin_ffs
|
||||
|
||||
/*
|
||||
* popcount*() functions to use for bitmapping.
|
||||
*/
|
||||
#define JEMALLOC_INTERNAL_POPCOUNTL __builtin_popcountl
|
||||
#define JEMALLOC_INTERNAL_POPCOUNT __builtin_popcount
|
||||
|
||||
/*
|
||||
* If defined, explicitly attempt to more uniformly distribute large allocation
|
||||
* pointer alignments across all cache indices.
|
||||
@ -246,6 +237,12 @@
|
||||
*/
|
||||
/* #undef JEMALLOC_LOG */
|
||||
|
||||
/*
|
||||
* If defined, use readlinkat() (instead of readlink()) to follow
|
||||
* /etc/malloc_conf.
|
||||
*/
|
||||
/* #undef JEMALLOC_READLINKAT */
|
||||
|
||||
/*
|
||||
* Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
|
||||
*/
|
||||
@ -364,4 +361,7 @@
|
||||
*/
|
||||
/* #undef JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE */
|
||||
|
||||
/* Performs additional safety checks when defined. */
|
||||
/* #undef JEMALLOC_OPT_SAFETY_CHECKS */
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_DEFS_H_ */
|
||||
|
@ -0,0 +1,9 @@
|
||||
#ifndef __clang__
|
||||
# undef JEMALLOC_INTERNAL_UNREACHABLE
|
||||
# define JEMALLOC_INTERNAL_UNREACHABLE abort
|
||||
|
||||
# undef JEMALLOC_C11_ATOMICS
|
||||
# undef JEMALLOC_GCC_ATOMIC_ATOMICS
|
||||
# undef JEMALLOC_GCC_U8_ATOMIC_ATOMICS
|
||||
# undef JEMALLOC_GCC_U8_SYNC_ATOMICS
|
||||
#endif
|
@ -2,7 +2,6 @@
|
||||
#define JEMALLOC_INTERNAL_EXTERNS_H
|
||||
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
/* TSD checks this to set thread local slow state accordingly. */
|
||||
@ -11,6 +10,7 @@ extern bool malloc_slow;
|
||||
/* Run-time options. */
|
||||
extern bool opt_abort;
|
||||
extern bool opt_abort_conf;
|
||||
extern bool opt_confirm_conf;
|
||||
extern const char *opt_junk;
|
||||
extern bool opt_junk_alloc;
|
||||
extern bool opt_junk_free;
|
||||
@ -25,6 +25,9 @@ extern unsigned ncpus;
|
||||
/* Number of arenas used for automatic multiplexing of threads and arenas. */
|
||||
extern unsigned narenas_auto;
|
||||
|
||||
/* Base index for manual arenas. */
|
||||
extern unsigned manual_arena_base;
|
||||
|
||||
/*
|
||||
* Arenas that are used to service external requests. Not all elements of the
|
||||
* arenas array are necessarily used; arenas are created lazily as needed.
|
||||
@ -49,5 +52,6 @@ void jemalloc_prefork(void);
|
||||
void jemalloc_postfork_parent(void);
|
||||
void jemalloc_postfork_child(void);
|
||||
bool malloc_initialized(void);
|
||||
void je_sdallocx_noflags(void *ptr, size_t size);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EXTERNS_H */
|
||||
|
@ -4,13 +4,15 @@
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE malloc_cpuid_t
|
||||
malloc_getcpu(void) {
|
||||
assert(have_percpu_arena);
|
||||
#if defined(JEMALLOC_HAVE_SCHED_GETCPU)
|
||||
#if defined(_WIN32)
|
||||
return GetCurrentProcessorNumber();
|
||||
#elif defined(JEMALLOC_HAVE_SCHED_GETCPU)
|
||||
return (malloc_cpuid_t)sched_getcpu();
|
||||
#else
|
||||
not_reached();
|
||||
@ -108,14 +110,14 @@ decay_ticker_get(tsd_t *tsd, unsigned ind) {
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE cache_bin_t *
|
||||
tcache_small_bin_get(tcache_t *tcache, szind_t binind) {
|
||||
assert(binind < NBINS);
|
||||
assert(binind < SC_NBINS);
|
||||
return &tcache->bins_small[binind];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE cache_bin_t *
|
||||
tcache_large_bin_get(tcache_t *tcache, szind_t binind) {
|
||||
assert(binind >= NBINS &&binind < nhbins);
|
||||
return &tcache->bins_large[binind - NBINS];
|
||||
assert(binind >= SC_NBINS &&binind < nhbins);
|
||||
return &tcache->bins_large[binind - SC_NBINS];
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
@ -156,7 +158,7 @@ pre_reentrancy(tsd_t *tsd, arena_t *arena) {
|
||||
if (fast) {
|
||||
/* Prepare slow path for reentrancy. */
|
||||
tsd_slow_update(tsd);
|
||||
assert(tsd->state == tsd_state_nominal_slow);
|
||||
assert(tsd_state_get(tsd) == tsd_state_nominal_slow);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,8 @@ arena_ichoose(tsd_t *tsd, arena_t *arena) {
|
||||
static inline bool
|
||||
arena_is_auto(arena_t *arena) {
|
||||
assert(narenas_auto > 0);
|
||||
return (arena_ind_get(arena) < narenas_auto);
|
||||
|
||||
return (arena_ind_get(arena) < manual_arena_base);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE extent_t *
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef JEMALLOC_INTERNAL_INLINES_C_H
|
||||
#define JEMALLOC_INTERNAL_INLINES_C_H
|
||||
|
||||
#include "jemalloc/internal/hook.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
#include "jemalloc/internal/witness.h"
|
||||
@ -42,7 +43,6 @@ iallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,
|
||||
bool is_internal, arena_t *arena, bool slow_path) {
|
||||
void *ret;
|
||||
|
||||
assert(size != 0);
|
||||
assert(!is_internal || tcache == NULL);
|
||||
assert(!is_internal || arena == NULL || arena_is_auto(arena));
|
||||
if (!tsdn_null(tsdn) && tsd_reentrancy_level_get(tsdn_tsd(tsdn)) == 0) {
|
||||
@ -133,31 +133,20 @@ isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, size_t alignment, bool zero, tcache_t *tcache,
|
||||
arena_t *arena) {
|
||||
size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
|
||||
hook_ralloc_args_t *hook_args) {
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
void *p;
|
||||
size_t usize, copysize;
|
||||
|
||||
usize = sz_sa2u(size + extra, alignment);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
usize = sz_sa2u(size, alignment);
|
||||
if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) {
|
||||
return NULL;
|
||||
}
|
||||
p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
||||
if (p == NULL) {
|
||||
if (extra == 0) {
|
||||
return NULL;
|
||||
}
|
||||
/* Try again, without extra this time. */
|
||||
usize = sz_sa2u(size, alignment);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
return NULL;
|
||||
}
|
||||
p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
||||
if (p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Copy at most size bytes (not size+extra), since the caller has no
|
||||
@ -165,13 +154,26 @@ iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
*/
|
||||
copysize = (size < oldsize) ? size : oldsize;
|
||||
memcpy(p, ptr, copysize);
|
||||
hook_invoke_alloc(hook_args->is_realloc
|
||||
? hook_alloc_realloc : hook_alloc_rallocx, p, (uintptr_t)p,
|
||||
hook_args->args);
|
||||
hook_invoke_dalloc(hook_args->is_realloc
|
||||
? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
|
||||
isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_realloc threads through the knowledge of whether or not this call comes
|
||||
* from je_realloc (as opposed to je_rallocx); this ensures that we pass the
|
||||
* correct entry point into any hooks.
|
||||
* Note that these functions are all force-inlined, so no actual bool gets
|
||||
* passed-around anywhere.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||
bool zero, tcache_t *tcache, arena_t *arena) {
|
||||
bool zero, tcache_t *tcache, arena_t *arena, hook_ralloc_args_t *hook_args)
|
||||
{
|
||||
assert(ptr != NULL);
|
||||
assert(size != 0);
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
@ -183,24 +185,24 @@ iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||
* Existing object alignment is inadequate; allocate new space
|
||||
* and copy.
|
||||
*/
|
||||
return iralloct_realign(tsdn, ptr, oldsize, size, 0, alignment,
|
||||
zero, tcache, arena);
|
||||
return iralloct_realign(tsdn, ptr, oldsize, size, alignment,
|
||||
zero, tcache, arena, hook_args);
|
||||
}
|
||||
|
||||
return arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,
|
||||
tcache);
|
||||
tcache, hook_args);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||
bool zero) {
|
||||
bool zero, hook_ralloc_args_t *hook_args) {
|
||||
return iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,
|
||||
tcache_get(tsd), NULL);
|
||||
tcache_get(tsd), NULL, hook_args);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
size_t alignment, bool zero) {
|
||||
size_t alignment, bool zero, size_t *newsize) {
|
||||
assert(ptr != NULL);
|
||||
assert(size != 0);
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
@ -209,10 +211,12 @@ ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
|
||||
!= 0) {
|
||||
/* Existing object alignment is inadequate. */
|
||||
*newsize = oldsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
return arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero);
|
||||
return arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero,
|
||||
newsize);
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_INLINES_C_H */
|
||||
|
@ -30,7 +30,7 @@
|
||||
# define restrict
|
||||
#endif
|
||||
|
||||
/* Various function pointers are statick and immutable except during testing. */
|
||||
/* Various function pointers are static and immutable except during testing. */
|
||||
#ifdef JEMALLOC_JET
|
||||
# define JET_MUTABLE
|
||||
#else
|
||||
@ -40,4 +40,75 @@
|
||||
#define JEMALLOC_VA_ARGS_HEAD(head, ...) head
|
||||
#define JEMALLOC_VA_ARGS_TAIL(head, ...) __VA_ARGS__
|
||||
|
||||
#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) \
|
||||
&& defined(JEMALLOC_HAVE_ATTR) && (__GNUC__ >= 7)
|
||||
#define JEMALLOC_FALLTHROUGH JEMALLOC_ATTR(fallthrough);
|
||||
#else
|
||||
#define JEMALLOC_FALLTHROUGH /* falls through */
|
||||
#endif
|
||||
|
||||
/* Diagnostic suppression macros */
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define JEMALLOC_DIAGNOSTIC_PUSH __pragma(warning(push))
|
||||
# define JEMALLOC_DIAGNOSTIC_POP __pragma(warning(pop))
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE(W) __pragma(warning(disable:W))
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
|
||||
# define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
/* #pragma GCC diagnostic first appeared in gcc 4.6. */
|
||||
#elif (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && \
|
||||
(__GNUC_MINOR__ > 5)))) || defined(__clang__)
|
||||
/*
|
||||
* The JEMALLOC_PRAGMA__ macro is an implementation detail of the GCC and Clang
|
||||
* diagnostic suppression macros and should not be used anywhere else.
|
||||
*/
|
||||
# define JEMALLOC_PRAGMA__(X) _Pragma(#X)
|
||||
# define JEMALLOC_DIAGNOSTIC_PUSH JEMALLOC_PRAGMA__(GCC diagnostic push)
|
||||
# define JEMALLOC_DIAGNOSTIC_POP JEMALLOC_PRAGMA__(GCC diagnostic pop)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE(W) \
|
||||
JEMALLOC_PRAGMA__(GCC diagnostic ignored W)
|
||||
|
||||
/*
|
||||
* The -Wmissing-field-initializers warning is buggy in GCC versions < 5.1 and
|
||||
* all clang versions up to version 7 (currently trunk, unreleased). This macro
|
||||
* suppresses the warning for the affected compiler versions only.
|
||||
*/
|
||||
# if ((defined(__GNUC__) && !defined(__clang__)) && (__GNUC__ < 5)) || \
|
||||
defined(__clang__)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Wmissing-field-initializers")
|
||||
# else
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
# endif
|
||||
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Wtype-limits")
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_UNUSED_PARAMETER \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Wunused-parameter")
|
||||
# if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 7)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE("-Walloc-size-larger-than=")
|
||||
# else
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
|
||||
# endif
|
||||
# define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS \
|
||||
JEMALLOC_DIAGNOSTIC_PUSH \
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE_UNUSED_PARAMETER
|
||||
#else
|
||||
# define JEMALLOC_DIAGNOSTIC_PUSH
|
||||
# define JEMALLOC_DIAGNOSTIC_POP
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE(W)
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
|
||||
# define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
|
||||
# define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Disables spurious diagnostics for all headers. Since these headers are not
|
||||
* included by users directly, it does not affect their diagnostic settings.
|
||||
*/
|
||||
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_MACROS_H */
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef JEMALLOC_INTERNAL_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_TYPES_H
|
||||
|
||||
#include "jemalloc/internal/quantum.h"
|
||||
|
||||
/* Page size index type. */
|
||||
typedef unsigned pszind_t;
|
||||
|
||||
@ -50,79 +52,6 @@ typedef int malloc_cpuid_t;
|
||||
/* Smallest size class to support. */
|
||||
#define TINY_MIN (1U << LG_TINY_MIN)
|
||||
|
||||
/*
|
||||
* Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
|
||||
* classes).
|
||||
*/
|
||||
#ifndef LG_QUANTUM
|
||||
# if (defined(__i386__) || defined(_M_IX86))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __ia64__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __alpha__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__sparc64__) || defined(__sparcv9) || defined(__sparc_v9__))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__amd64__) || defined(__x86_64__) || defined(_M_X64))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __arm__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __aarch64__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __hppa__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __m68k__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __mips__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __nios2__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __or1k__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __powerpc__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if defined(__riscv) || defined(__riscv__)
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __s390__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined (__SH3E__) || defined(__SH4_SINGLE__) || defined(__SH4__) || \
|
||||
defined(__SH4_SINGLE_ONLY__))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __tile__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __le32__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifndef LG_QUANTUM
|
||||
# error "Unknown minimum alignment for architecture; specify via "
|
||||
"--with-lg-quantum"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define QUANTUM ((size_t)(1U << LG_QUANTUM))
|
||||
#define QUANTUM_MASK (QUANTUM - 1)
|
||||
|
||||
/* Return the smallest quantum multiple that is >= a. */
|
||||
#define QUANTUM_CEILING(a) \
|
||||
(((a) + QUANTUM_MASK) & ~QUANTUM_MASK)
|
||||
|
||||
#define LONG ((size_t)(1U << LG_SIZEOF_LONG))
|
||||
#define LONG_MASK (LONG - 1)
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
# include "../jemalloc.h"
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_OSATOMIC) || defined(JEMALLOC_OSSPIN))
|
||||
#if defined(JEMALLOC_OSATOMIC)
|
||||
#include <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
# include "jemalloc/internal/private_namespace_jet.h"
|
||||
# endif
|
||||
#endif
|
||||
#include "jemalloc/internal/hooks.h"
|
||||
#include "jemalloc/internal/test_hooks.h"
|
||||
|
||||
#ifdef JEMALLOC_DEFINE_MADVISE_FREE
|
||||
# define JEMALLOC_MADV_FREE 8
|
||||
@ -158,7 +158,26 @@ static const bool config_log =
|
||||
false
|
||||
#endif
|
||||
;
|
||||
#ifdef JEMALLOC_HAVE_SCHED_GETCPU
|
||||
/*
|
||||
* Are extra safety checks enabled; things like checking the size of sized
|
||||
* deallocations, double-frees, etc.
|
||||
*/
|
||||
static const bool config_opt_safety_checks =
|
||||
#ifdef JEMALLOC_OPT_SAFETY_CHECKS
|
||||
true
|
||||
#elif defined(JEMALLOC_DEBUG)
|
||||
/*
|
||||
* This lets us only guard safety checks by one flag instead of two; fast
|
||||
* checks can guard solely by config_opt_safety_checks and run in debug mode
|
||||
* too.
|
||||
*/
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
;
|
||||
|
||||
#if defined(_WIN32) || defined(JEMALLOC_HAVE_SCHED_GETCPU)
|
||||
/* Currently percpu_arena depends on sched_getcpu. */
|
||||
#define JEMALLOC_PERCPU_ARENA
|
||||
#endif
|
||||
|
@ -1,13 +1,16 @@
|
||||
#ifndef JEMALLOC_INTERNAL_LARGE_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_LARGE_EXTERNS_H
|
||||
|
||||
#include "jemalloc/internal/hook.h"
|
||||
|
||||
void *large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);
|
||||
void *large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||
bool zero);
|
||||
bool large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
|
||||
size_t usize_max, bool zero);
|
||||
void *large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache);
|
||||
void *large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache,
|
||||
hook_ralloc_args_t *hook_args);
|
||||
|
||||
typedef void (large_dalloc_junk_t)(void *, size_t);
|
||||
extern large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk;
|
||||
@ -23,4 +26,7 @@ prof_tctx_t *large_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent);
|
||||
void large_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx);
|
||||
void large_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent);
|
||||
|
||||
nstime_t large_prof_alloc_time_get(const extent_t *extent);
|
||||
void large_prof_alloc_time_set(extent_t *extent, nstime_t time);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_LARGE_EXTERNS_H */
|
||||
|
@ -54,7 +54,7 @@ size_t malloc_vsnprintf(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);
|
||||
/*
|
||||
* The caller can set write_cb and cbopaque to null to choose to print with the
|
||||
* The caller can set write_cb to null to choose to print with the
|
||||
* je_malloc_message hook.
|
||||
*/
|
||||
void malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
|
@ -37,14 +37,17 @@ struct malloc_mutex_s {
|
||||
# endif
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
os_unfair_lock lock;
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
OSSpinLock lock;
|
||||
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
|
||||
pthread_mutex_t lock;
|
||||
malloc_mutex_t *postponed_next;
|
||||
#else
|
||||
pthread_mutex_t lock;
|
||||
#endif
|
||||
/*
|
||||
* Hint flag to avoid exclusive cache line contention
|
||||
* during spin waiting
|
||||
*/
|
||||
atomic_b_t locked;
|
||||
};
|
||||
/*
|
||||
* We only touch witness when configured w/ debug. However we
|
||||
@ -84,10 +87,6 @@ struct malloc_mutex_s {
|
||||
# define MALLOC_MUTEX_LOCK(m) os_unfair_lock_lock(&(m)->lock)
|
||||
# define MALLOC_MUTEX_UNLOCK(m) os_unfair_lock_unlock(&(m)->lock)
|
||||
# define MALLOC_MUTEX_TRYLOCK(m) (!os_unfair_lock_trylock(&(m)->lock))
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
# define MALLOC_MUTEX_LOCK(m) OSSpinLockLock(&(m)->lock)
|
||||
# define MALLOC_MUTEX_UNLOCK(m) OSSpinLockUnlock(&(m)->lock)
|
||||
# define MALLOC_MUTEX_TRYLOCK(m) (!OSSpinLockTry(&(m)->lock))
|
||||
#else
|
||||
# define MALLOC_MUTEX_LOCK(m) pthread_mutex_lock(&(m)->lock)
|
||||
# define MALLOC_MUTEX_UNLOCK(m) pthread_mutex_unlock(&(m)->lock)
|
||||
@ -101,22 +100,37 @@ struct malloc_mutex_s {
|
||||
#ifdef _WIN32
|
||||
# define MALLOC_MUTEX_INITIALIZER
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, 0}}, \
|
||||
# if defined(JEMALLOC_DEBUG)
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT, ATOMIC_INIT(false)}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT), 0}
|
||||
# else
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT, ATOMIC_INIT(false)}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
|
||||
# endif
|
||||
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
|
||||
# if (defined(JEMALLOC_DEBUG))
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL, ATOMIC_INIT(false)}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT), 0}
|
||||
# else
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL, ATOMIC_INIT(false)}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
|
||||
# endif
|
||||
|
||||
#else
|
||||
# define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT
|
||||
# if defined(JEMALLOC_DEBUG)
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, ATOMIC_INIT(false)}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT), 0}
|
||||
# else
|
||||
# define MALLOC_MUTEX_INITIALIZER \
|
||||
{{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, ATOMIC_INIT(false)}}, \
|
||||
WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef JEMALLOC_LAZY_LOCK
|
||||
@ -137,6 +151,7 @@ void malloc_mutex_lock_slow(malloc_mutex_t *mutex);
|
||||
static inline void
|
||||
malloc_mutex_lock_final(malloc_mutex_t *mutex) {
|
||||
MALLOC_MUTEX_LOCK(mutex);
|
||||
atomic_store_b(&mutex->locked, true, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
@ -162,6 +177,7 @@ malloc_mutex_trylock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
|
||||
witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
|
||||
if (isthreaded) {
|
||||
if (malloc_mutex_trylock_final(mutex)) {
|
||||
atomic_store_b(&mutex->locked, true, ATOMIC_RELAXED);
|
||||
return true;
|
||||
}
|
||||
mutex_owner_stats_update(tsdn, mutex);
|
||||
@ -201,6 +217,7 @@ malloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
|
||||
if (isthreaded) {
|
||||
if (malloc_mutex_trylock_final(mutex)) {
|
||||
malloc_mutex_lock_slow(mutex);
|
||||
atomic_store_b(&mutex->locked, true, ATOMIC_RELAXED);
|
||||
}
|
||||
mutex_owner_stats_update(tsdn, mutex);
|
||||
}
|
||||
@ -209,6 +226,7 @@ malloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
|
||||
|
||||
static inline void
|
||||
malloc_mutex_unlock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
|
||||
atomic_store_b(&mutex->locked, false, ATOMIC_RELAXED);
|
||||
witness_unlock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
|
||||
if (isthreaded) {
|
||||
MALLOC_MUTEX_UNLOCK(mutex);
|
||||
@ -243,4 +261,26 @@ malloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,
|
||||
atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline void
|
||||
malloc_mutex_prof_accum(tsdn_t *tsdn, mutex_prof_data_t *data,
|
||||
malloc_mutex_t *mutex) {
|
||||
mutex_prof_data_t *source = &mutex->prof_data;
|
||||
/* Can only read holding the mutex. */
|
||||
malloc_mutex_assert_owner(tsdn, mutex);
|
||||
|
||||
nstime_add(&data->tot_wait_time, &source->tot_wait_time);
|
||||
if (nstime_compare(&source->max_wait_time, &data->max_wait_time) > 0) {
|
||||
nstime_copy(&data->max_wait_time, &source->max_wait_time);
|
||||
}
|
||||
data->n_wait_times += source->n_wait_times;
|
||||
data->n_spin_acquired += source->n_spin_acquired;
|
||||
if (data->max_n_thds < source->max_n_thds) {
|
||||
data->max_n_thds = source->max_n_thds;
|
||||
}
|
||||
/* n_wait_thds is not reported. */
|
||||
atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
|
||||
data->n_owner_switches += source->n_owner_switches;
|
||||
data->n_lock_ops += source->n_lock_ops;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_MUTEX_H */
|
||||
|
@ -35,22 +35,31 @@ typedef enum {
|
||||
mutex_prof_num_arena_mutexes
|
||||
} mutex_prof_arena_ind_t;
|
||||
|
||||
/*
|
||||
* The forth parameter is a boolean value that is true for derived rate counters
|
||||
* and false for real ones.
|
||||
*/
|
||||
#define MUTEX_PROF_UINT64_COUNTERS \
|
||||
OP(num_ops, uint64_t, "n_lock_ops") \
|
||||
OP(num_wait, uint64_t, "n_waiting") \
|
||||
OP(num_spin_acq, uint64_t, "n_spin_acq") \
|
||||
OP(num_owner_switch, uint64_t, "n_owner_switch") \
|
||||
OP(total_wait_time, uint64_t, "total_wait_ns") \
|
||||
OP(max_wait_time, uint64_t, "max_wait_ns")
|
||||
OP(num_ops, uint64_t, "n_lock_ops", false, num_ops) \
|
||||
OP(num_ops_ps, uint64_t, "(#/sec)", true, num_ops) \
|
||||
OP(num_wait, uint64_t, "n_waiting", false, num_wait) \
|
||||
OP(num_wait_ps, uint64_t, "(#/sec)", true, num_wait) \
|
||||
OP(num_spin_acq, uint64_t, "n_spin_acq", false, num_spin_acq) \
|
||||
OP(num_spin_acq_ps, uint64_t, "(#/sec)", true, num_spin_acq) \
|
||||
OP(num_owner_switch, uint64_t, "n_owner_switch", false, num_owner_switch) \
|
||||
OP(num_owner_switch_ps, uint64_t, "(#/sec)", true, num_owner_switch) \
|
||||
OP(total_wait_time, uint64_t, "total_wait_ns", false, total_wait_time) \
|
||||
OP(total_wait_time_ps, uint64_t, "(#/sec)", true, total_wait_time) \
|
||||
OP(max_wait_time, uint64_t, "max_wait_ns", false, max_wait_time)
|
||||
|
||||
#define MUTEX_PROF_UINT32_COUNTERS \
|
||||
OP(max_num_thds, uint32_t, "max_n_thds")
|
||||
OP(max_num_thds, uint32_t, "max_n_thds", false, max_num_thds)
|
||||
|
||||
#define MUTEX_PROF_COUNTERS \
|
||||
MUTEX_PROF_UINT64_COUNTERS \
|
||||
MUTEX_PROF_UINT32_COUNTERS
|
||||
|
||||
#define OP(counter, type, human) mutex_counter_##counter,
|
||||
#define OP(counter, type, human, derived, base_counter) mutex_counter_##counter,
|
||||
|
||||
#define COUNTER_ENUM(counter_list, t) \
|
||||
typedef enum { \
|
||||
|
@ -12,15 +12,20 @@
|
||||
#define bootstrap_calloc JEMALLOC_N(bootstrap_calloc)
|
||||
#define bootstrap_free JEMALLOC_N(bootstrap_free)
|
||||
#define bootstrap_malloc JEMALLOC_N(bootstrap_malloc)
|
||||
#define free_default JEMALLOC_N(free_default)
|
||||
#define iarena_cleanup JEMALLOC_N(iarena_cleanup)
|
||||
#define je_sdallocx_noflags JEMALLOC_N(je_sdallocx_noflags)
|
||||
#define jemalloc_postfork_child JEMALLOC_N(jemalloc_postfork_child)
|
||||
#define malloc_default JEMALLOC_N(malloc_default)
|
||||
#define malloc_initialized JEMALLOC_N(malloc_initialized)
|
||||
#define malloc_slow JEMALLOC_N(malloc_slow)
|
||||
#define manual_arena_base JEMALLOC_N(manual_arena_base)
|
||||
#define narenas_auto JEMALLOC_N(narenas_auto)
|
||||
#define narenas_total_get JEMALLOC_N(narenas_total_get)
|
||||
#define ncpus JEMALLOC_N(ncpus)
|
||||
#define opt_abort JEMALLOC_N(opt_abort)
|
||||
#define opt_abort_conf JEMALLOC_N(opt_abort_conf)
|
||||
#define opt_confirm_conf JEMALLOC_N(opt_confirm_conf)
|
||||
#define opt_junk JEMALLOC_N(opt_junk)
|
||||
#define opt_junk_alloc JEMALLOC_N(opt_junk_alloc)
|
||||
#define opt_junk_free JEMALLOC_N(opt_junk_free)
|
||||
@ -28,9 +33,12 @@
|
||||
#define opt_utrace JEMALLOC_N(opt_utrace)
|
||||
#define opt_xmalloc JEMALLOC_N(opt_xmalloc)
|
||||
#define opt_zero JEMALLOC_N(opt_zero)
|
||||
#define sdallocx_default JEMALLOC_N(sdallocx_default)
|
||||
#define arena_alloc_junk_small JEMALLOC_N(arena_alloc_junk_small)
|
||||
#define arena_basic_stats_merge JEMALLOC_N(arena_basic_stats_merge)
|
||||
#define arena_bin_choose_lock JEMALLOC_N(arena_bin_choose_lock)
|
||||
#define arena_boot JEMALLOC_N(arena_boot)
|
||||
#define arena_choose_huge JEMALLOC_N(arena_choose_huge)
|
||||
#define arena_dalloc_bin_junked_locked JEMALLOC_N(arena_dalloc_bin_junked_locked)
|
||||
#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small)
|
||||
#define arena_dalloc_promoted JEMALLOC_N(arena_dalloc_promoted)
|
||||
@ -49,6 +57,8 @@
|
||||
#define arena_extent_ralloc_large_shrink JEMALLOC_N(arena_extent_ralloc_large_shrink)
|
||||
#define arena_extent_sn_next JEMALLOC_N(arena_extent_sn_next)
|
||||
#define arena_extents_dirty_dalloc JEMALLOC_N(arena_extents_dirty_dalloc)
|
||||
#define arena_init_huge JEMALLOC_N(arena_init_huge)
|
||||
#define arena_is_huge JEMALLOC_N(arena_is_huge)
|
||||
#define arena_malloc_hard JEMALLOC_N(arena_malloc_hard)
|
||||
#define arena_muzzy_decay_ms_default_get JEMALLOC_N(arena_muzzy_decay_ms_default_get)
|
||||
#define arena_muzzy_decay_ms_default_set JEMALLOC_N(arena_muzzy_decay_ms_default_set)
|
||||
@ -79,7 +89,9 @@
|
||||
#define h_steps JEMALLOC_N(h_steps)
|
||||
#define opt_dirty_decay_ms JEMALLOC_N(opt_dirty_decay_ms)
|
||||
#define opt_muzzy_decay_ms JEMALLOC_N(opt_muzzy_decay_ms)
|
||||
#define opt_oversize_threshold JEMALLOC_N(opt_oversize_threshold)
|
||||
#define opt_percpu_arena JEMALLOC_N(opt_percpu_arena)
|
||||
#define oversize_threshold JEMALLOC_N(oversize_threshold)
|
||||
#define percpu_arena_mode_names JEMALLOC_N(percpu_arena_mode_names)
|
||||
#define background_thread_boot0 JEMALLOC_N(background_thread_boot0)
|
||||
#define background_thread_boot1 JEMALLOC_N(background_thread_boot1)
|
||||
@ -96,7 +108,6 @@
|
||||
#define background_thread_stats_read JEMALLOC_N(background_thread_stats_read)
|
||||
#define background_threads_disable JEMALLOC_N(background_threads_disable)
|
||||
#define background_threads_enable JEMALLOC_N(background_threads_enable)
|
||||
#define can_enable_background_thread JEMALLOC_N(can_enable_background_thread)
|
||||
#define max_background_threads JEMALLOC_N(max_background_threads)
|
||||
#define n_background_threads JEMALLOC_N(n_background_threads)
|
||||
#define opt_background_thread JEMALLOC_N(opt_background_thread)
|
||||
@ -116,11 +127,14 @@
|
||||
#define base_stats_get JEMALLOC_N(base_stats_get)
|
||||
#define metadata_thp_mode_names JEMALLOC_N(metadata_thp_mode_names)
|
||||
#define opt_metadata_thp JEMALLOC_N(opt_metadata_thp)
|
||||
#define bin_boot JEMALLOC_N(bin_boot)
|
||||
#define bin_infos JEMALLOC_N(bin_infos)
|
||||
#define bin_init JEMALLOC_N(bin_init)
|
||||
#define bin_postfork_child JEMALLOC_N(bin_postfork_child)
|
||||
#define bin_postfork_parent JEMALLOC_N(bin_postfork_parent)
|
||||
#define bin_prefork JEMALLOC_N(bin_prefork)
|
||||
#define bin_shard_sizes_boot JEMALLOC_N(bin_shard_sizes_boot)
|
||||
#define bin_update_shard_size JEMALLOC_N(bin_update_shard_size)
|
||||
#define bitmap_info_init JEMALLOC_N(bitmap_info_init)
|
||||
#define bitmap_init JEMALLOC_N(bitmap_init)
|
||||
#define bitmap_size JEMALLOC_N(bitmap_size)
|
||||
@ -176,10 +190,14 @@
|
||||
#define extent_purge_forced_wrapper JEMALLOC_N(extent_purge_forced_wrapper)
|
||||
#define extent_purge_lazy_wrapper JEMALLOC_N(extent_purge_lazy_wrapper)
|
||||
#define extent_split_wrapper JEMALLOC_N(extent_split_wrapper)
|
||||
#define extent_util_stats_get JEMALLOC_N(extent_util_stats_get)
|
||||
#define extent_util_stats_verbose_get JEMALLOC_N(extent_util_stats_verbose_get)
|
||||
#define extents_alloc JEMALLOC_N(extents_alloc)
|
||||
#define extents_dalloc JEMALLOC_N(extents_dalloc)
|
||||
#define extents_evict JEMALLOC_N(extents_evict)
|
||||
#define extents_init JEMALLOC_N(extents_init)
|
||||
#define extents_nbytes_get JEMALLOC_N(extents_nbytes_get)
|
||||
#define extents_nextents_get JEMALLOC_N(extents_nextents_get)
|
||||
#define extents_npages_get JEMALLOC_N(extents_npages_get)
|
||||
#define extents_postfork_child JEMALLOC_N(extents_postfork_child)
|
||||
#define extents_postfork_parent JEMALLOC_N(extents_postfork_parent)
|
||||
@ -198,8 +216,12 @@
|
||||
#define extent_alloc_mmap JEMALLOC_N(extent_alloc_mmap)
|
||||
#define extent_dalloc_mmap JEMALLOC_N(extent_dalloc_mmap)
|
||||
#define opt_retain JEMALLOC_N(opt_retain)
|
||||
#define hooks_arena_new_hook JEMALLOC_N(hooks_arena_new_hook)
|
||||
#define hooks_libc_hook JEMALLOC_N(hooks_libc_hook)
|
||||
#define hook_boot JEMALLOC_N(hook_boot)
|
||||
#define hook_install JEMALLOC_N(hook_install)
|
||||
#define hook_invoke_alloc JEMALLOC_N(hook_invoke_alloc)
|
||||
#define hook_invoke_dalloc JEMALLOC_N(hook_invoke_dalloc)
|
||||
#define hook_invoke_expand JEMALLOC_N(hook_invoke_expand)
|
||||
#define hook_remove JEMALLOC_N(hook_remove)
|
||||
#define large_dalloc JEMALLOC_N(large_dalloc)
|
||||
#define large_dalloc_finish JEMALLOC_N(large_dalloc_finish)
|
||||
#define large_dalloc_junk JEMALLOC_N(large_dalloc_junk)
|
||||
@ -207,6 +229,8 @@
|
||||
#define large_dalloc_prep_junked_locked JEMALLOC_N(large_dalloc_prep_junked_locked)
|
||||
#define large_malloc JEMALLOC_N(large_malloc)
|
||||
#define large_palloc JEMALLOC_N(large_palloc)
|
||||
#define large_prof_alloc_time_get JEMALLOC_N(large_prof_alloc_time_get)
|
||||
#define large_prof_alloc_time_set JEMALLOC_N(large_prof_alloc_time_set)
|
||||
#define large_prof_tctx_get JEMALLOC_N(large_prof_tctx_get)
|
||||
#define large_prof_tctx_reset JEMALLOC_N(large_prof_tctx_reset)
|
||||
#define large_prof_tctx_set JEMALLOC_N(large_prof_tctx_set)
|
||||
@ -275,6 +299,7 @@
|
||||
#define opt_prof_final JEMALLOC_N(opt_prof_final)
|
||||
#define opt_prof_gdump JEMALLOC_N(opt_prof_gdump)
|
||||
#define opt_prof_leak JEMALLOC_N(opt_prof_leak)
|
||||
#define opt_prof_log JEMALLOC_N(opt_prof_log)
|
||||
#define opt_prof_prefix JEMALLOC_N(opt_prof_prefix)
|
||||
#define opt_prof_thread_active_init JEMALLOC_N(opt_prof_thread_active_init)
|
||||
#define prof_accum_init JEMALLOC_N(prof_accum_init)
|
||||
@ -295,6 +320,9 @@
|
||||
#define prof_gdump_val JEMALLOC_N(prof_gdump_val)
|
||||
#define prof_idump JEMALLOC_N(prof_idump)
|
||||
#define prof_interval JEMALLOC_N(prof_interval)
|
||||
#define prof_log_start JEMALLOC_N(prof_log_start)
|
||||
#define prof_log_stop JEMALLOC_N(prof_log_stop)
|
||||
#define prof_logging_state JEMALLOC_N(prof_logging_state)
|
||||
#define prof_lookup JEMALLOC_N(prof_lookup)
|
||||
#define prof_malloc_sample_object JEMALLOC_N(prof_malloc_sample_object)
|
||||
#define prof_mdump JEMALLOC_N(prof_mdump)
|
||||
@ -320,11 +348,18 @@
|
||||
#define rtree_new JEMALLOC_N(rtree_new)
|
||||
#define rtree_node_alloc JEMALLOC_N(rtree_node_alloc)
|
||||
#define rtree_node_dalloc JEMALLOC_N(rtree_node_dalloc)
|
||||
#define safety_check_fail JEMALLOC_N(safety_check_fail)
|
||||
#define safety_check_set_abort JEMALLOC_N(safety_check_set_abort)
|
||||
#define arena_mutex_names JEMALLOC_N(arena_mutex_names)
|
||||
#define global_mutex_names JEMALLOC_N(global_mutex_names)
|
||||
#define opt_stats_print JEMALLOC_N(opt_stats_print)
|
||||
#define opt_stats_print_opts JEMALLOC_N(opt_stats_print_opts)
|
||||
#define stats_print JEMALLOC_N(stats_print)
|
||||
#define sc_boot JEMALLOC_N(sc_boot)
|
||||
#define sc_data_global JEMALLOC_N(sc_data_global)
|
||||
#define sc_data_init JEMALLOC_N(sc_data_init)
|
||||
#define sc_data_update_slab_size JEMALLOC_N(sc_data_update_slab_size)
|
||||
#define sz_boot JEMALLOC_N(sz_boot)
|
||||
#define sz_index2size_tab JEMALLOC_N(sz_index2size_tab)
|
||||
#define sz_pind2sz_tab JEMALLOC_N(sz_pind2sz_tab)
|
||||
#define sz_size2index_tab JEMALLOC_N(sz_size2index_tab)
|
||||
@ -354,6 +389,8 @@
|
||||
#define tcaches_flush JEMALLOC_N(tcaches_flush)
|
||||
#define tsd_tcache_data_init JEMALLOC_N(tsd_tcache_data_init)
|
||||
#define tsd_tcache_enabled_data_init JEMALLOC_N(tsd_tcache_enabled_data_init)
|
||||
#define test_hooks_arena_new_hook JEMALLOC_N(test_hooks_arena_new_hook)
|
||||
#define test_hooks_libc_hook JEMALLOC_N(test_hooks_libc_hook)
|
||||
#define malloc_tsd_boot0 JEMALLOC_N(malloc_tsd_boot0)
|
||||
#define malloc_tsd_boot1 JEMALLOC_N(malloc_tsd_boot1)
|
||||
#define malloc_tsd_cleanup_register JEMALLOC_N(malloc_tsd_cleanup_register)
|
||||
@ -362,8 +399,15 @@
|
||||
#define tsd_booted JEMALLOC_N(tsd_booted)
|
||||
#define tsd_cleanup JEMALLOC_N(tsd_cleanup)
|
||||
#define tsd_fetch_slow JEMALLOC_N(tsd_fetch_slow)
|
||||
#define tsd_global_slow JEMALLOC_N(tsd_global_slow)
|
||||
#define tsd_global_slow_dec JEMALLOC_N(tsd_global_slow_dec)
|
||||
#define tsd_global_slow_inc JEMALLOC_N(tsd_global_slow_inc)
|
||||
#define tsd_initialized JEMALLOC_N(tsd_initialized)
|
||||
#define tsd_postfork_child JEMALLOC_N(tsd_postfork_child)
|
||||
#define tsd_postfork_parent JEMALLOC_N(tsd_postfork_parent)
|
||||
#define tsd_prefork JEMALLOC_N(tsd_prefork)
|
||||
#define tsd_slow_update JEMALLOC_N(tsd_slow_update)
|
||||
#define tsd_state_set JEMALLOC_N(tsd_state_set)
|
||||
#define tsd_tls JEMALLOC_N(tsd_tls)
|
||||
#define witness_depth_error JEMALLOC_N(witness_depth_error)
|
||||
#define witness_init JEMALLOC_N(witness_init)
|
||||
|
@ -14,6 +14,7 @@ extern bool opt_prof_gdump; /* High-water memory dumping. */
|
||||
extern bool opt_prof_final; /* Final profile dumping. */
|
||||
extern bool opt_prof_leak; /* Dump leak summary at exit. */
|
||||
extern bool opt_prof_accum; /* Report cumulative bytes. */
|
||||
extern bool opt_prof_log; /* Turn logging on at boot. */
|
||||
extern char opt_prof_prefix[
|
||||
/* Minimize memory bloat for non-prof builds. */
|
||||
#ifdef JEMALLOC_PROF
|
||||
@ -45,7 +46,8 @@ extern size_t lg_prof_sample;
|
||||
void prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated);
|
||||
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 prof_free_sampled_object(tsd_t *tsd, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx);
|
||||
void bt_init(prof_bt_t *bt, void **vec);
|
||||
void prof_backtrace(prof_bt_t *bt);
|
||||
prof_tctx_t *prof_lookup(tsd_t *tsd, prof_bt_t *bt);
|
||||
@ -89,4 +91,15 @@ void prof_postfork_parent(tsdn_t *tsdn);
|
||||
void prof_postfork_child(tsdn_t *tsdn);
|
||||
void prof_sample_threshold_update(prof_tdata_t *tdata);
|
||||
|
||||
bool prof_log_start(tsdn_t *tsdn, const char *filename);
|
||||
bool prof_log_stop(tsdn_t *tsdn);
|
||||
#ifdef JEMALLOC_JET
|
||||
size_t prof_log_bt_count(void);
|
||||
size_t prof_log_alloc_count(void);
|
||||
size_t prof_log_thr_count(void);
|
||||
bool prof_log_is_logging(void);
|
||||
bool prof_log_rep_check(void);
|
||||
void prof_log_dummy_set(bool new_value);
|
||||
#endif
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_PROF_EXTERNS_H */
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
|
||||
static inline bool
|
||||
prof_accum_add(tsdn_t *tsdn, prof_accum_t *prof_accum, uint64_t accumbytes) {
|
||||
prof_accum_add(tsdn_t *tsdn, prof_accum_t *prof_accum,
|
||||
uint64_t accumbytes) {
|
||||
cassert(config_prof);
|
||||
|
||||
bool overflow;
|
||||
@ -42,7 +43,8 @@ prof_accum_add(tsdn_t *tsdn, prof_accum_t *prof_accum, uint64_t accumbytes) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
prof_accum_cancel(tsdn_t *tsdn, prof_accum_t *prof_accum, size_t usize) {
|
||||
prof_accum_cancel(tsdn_t *tsdn, prof_accum_t *prof_accum,
|
||||
size_t usize) {
|
||||
cassert(config_prof);
|
||||
|
||||
/*
|
||||
@ -55,15 +57,15 @@ prof_accum_cancel(tsdn_t *tsdn, prof_accum_t *prof_accum, size_t usize) {
|
||||
#ifdef JEMALLOC_ATOMIC_U64
|
||||
a0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);
|
||||
do {
|
||||
a1 = (a0 >= LARGE_MINCLASS - usize) ? a0 - (LARGE_MINCLASS -
|
||||
usize) : 0;
|
||||
a1 = (a0 >= SC_LARGE_MINCLASS - usize)
|
||||
? a0 - (SC_LARGE_MINCLASS - usize) : 0;
|
||||
} while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,
|
||||
a1, ATOMIC_RELAXED, ATOMIC_RELAXED));
|
||||
#else
|
||||
malloc_mutex_lock(tsdn, &prof_accum->mtx);
|
||||
a0 = prof_accum->accumbytes;
|
||||
a1 = (a0 >= LARGE_MINCLASS - usize) ? a0 - (LARGE_MINCLASS - usize) :
|
||||
0;
|
||||
a1 = (a0 >= SC_LARGE_MINCLASS - usize)
|
||||
? a0 - (SC_LARGE_MINCLASS - usize) : 0;
|
||||
prof_accum->accumbytes = a1;
|
||||
malloc_mutex_unlock(tsdn, &prof_accum->mtx);
|
||||
#endif
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H
|
||||
#define JEMALLOC_INTERNAL_PROF_INLINES_B_H
|
||||
|
||||
#include "jemalloc/internal/safety_check.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
@ -61,13 +62,54 @@ prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {
|
||||
arena_prof_tctx_reset(tsdn, ptr, tctx);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE nstime_t
|
||||
prof_alloc_time_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
return arena_prof_alloc_time_get(tsdn, ptr, alloc_ctx);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
prof_alloc_time_set(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx,
|
||||
nstime_t t) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
arena_prof_alloc_time_set(tsdn, ptr, alloc_ctx, t);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
prof_sample_check(tsd_t *tsd, size_t usize, bool update) {
|
||||
ssize_t check = update ? 0 : usize;
|
||||
|
||||
int64_t bytes_until_sample = tsd_bytes_until_sample_get(tsd);
|
||||
if (update) {
|
||||
bytes_until_sample -= usize;
|
||||
if (tsd_nominal(tsd)) {
|
||||
tsd_bytes_until_sample_set(tsd, bytes_until_sample);
|
||||
}
|
||||
}
|
||||
if (likely(bytes_until_sample >= check)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
prof_sample_accum_update(tsd_t *tsd, size_t usize, bool update,
|
||||
prof_tdata_t **tdata_out) {
|
||||
prof_tdata_t **tdata_out) {
|
||||
prof_tdata_t *tdata;
|
||||
|
||||
cassert(config_prof);
|
||||
|
||||
/* Fastpath: no need to load tdata */
|
||||
if (likely(prof_sample_check(tsd, usize, update))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool booted = tsd_prof_tdata_get(tsd);
|
||||
tdata = prof_tdata_get(tsd, true);
|
||||
if (unlikely((uintptr_t)tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)) {
|
||||
tdata = NULL;
|
||||
@ -81,21 +123,23 @@ prof_sample_accum_update(tsd_t *tsd, size_t usize, bool update,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (likely(tdata->bytes_until_sample >= usize)) {
|
||||
if (update) {
|
||||
tdata->bytes_until_sample -= usize;
|
||||
}
|
||||
/*
|
||||
* If this was the first creation of tdata, then
|
||||
* prof_tdata_get() reset bytes_until_sample, so decrement and
|
||||
* check it again
|
||||
*/
|
||||
if (!booted && prof_sample_check(tsd, usize, update)) {
|
||||
return true;
|
||||
} else {
|
||||
if (tsd_reentrancy_level_get(tsd) > 0) {
|
||||
return true;
|
||||
}
|
||||
/* Compute new sample threshold. */
|
||||
if (update) {
|
||||
prof_sample_threshold_update(tdata);
|
||||
}
|
||||
return !tdata->active;
|
||||
}
|
||||
|
||||
if (tsd_reentrancy_level_get(tsd) > 0) {
|
||||
return true;
|
||||
}
|
||||
/* Compute new sample threshold. */
|
||||
if (update) {
|
||||
prof_sample_threshold_update(tdata);
|
||||
}
|
||||
return !tdata->active;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE prof_tctx_t *
|
||||
@ -187,7 +231,7 @@ prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
|
||||
* counters.
|
||||
*/
|
||||
if (unlikely(old_sampled)) {
|
||||
prof_free_sampled_object(tsd, old_usize, old_tctx);
|
||||
prof_free_sampled_object(tsd, ptr, old_usize, old_tctx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +243,7 @@ prof_free(tsd_t *tsd, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx) {
|
||||
assert(usize == isalloc(tsd_tsdn(tsd), ptr));
|
||||
|
||||
if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {
|
||||
prof_free_sampled_object(tsd, usize, tctx);
|
||||
prof_free_sampled_object(tsd, ptr, usize, tctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,6 @@ struct prof_tdata_s {
|
||||
|
||||
/* Sampling state. */
|
||||
uint64_t prng_state;
|
||||
uint64_t bytes_until_sample;
|
||||
|
||||
/* State used to avoid dumping while operating on prof internals. */
|
||||
bool enq;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define je_malloc_stats_print JEMALLOC_N(malloc_stats_print)
|
||||
#define je_malloc_usable_size JEMALLOC_N(malloc_usable_size)
|
||||
#define je_mallocx JEMALLOC_N(mallocx)
|
||||
#define je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 JEMALLOC_N(smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756)
|
||||
#define je_nallocx JEMALLOC_N(nallocx)
|
||||
#define je_posix_memalign JEMALLOC_N(posix_memalign)
|
||||
#define je_rallocx JEMALLOC_N(rallocx)
|
||||
|
77
contrib/jemalloc/include/jemalloc/internal/quantum.h
Normal file
77
contrib/jemalloc/include/jemalloc/internal/quantum.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef JEMALLOC_INTERNAL_QUANTUM_H
|
||||
#define JEMALLOC_INTERNAL_QUANTUM_H
|
||||
|
||||
/*
|
||||
* Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
|
||||
* classes).
|
||||
*/
|
||||
#ifndef LG_QUANTUM
|
||||
# if (defined(__i386__) || defined(_M_IX86))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __ia64__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __alpha__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__sparc64__) || defined(__sparcv9) || defined(__sparc_v9__))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined(__amd64__) || defined(__x86_64__) || defined(_M_X64))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __arm__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __aarch64__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __hppa__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __m68k__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __mips__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __nios2__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __or1k__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __powerpc__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if defined(__riscv) || defined(__riscv__)
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __s390__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# if (defined (__SH3E__) || defined(__SH4_SINGLE__) || defined(__SH4__) || \
|
||||
defined(__SH4_SINGLE_ONLY__))
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __tile__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __le32__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifndef LG_QUANTUM
|
||||
# error "Unknown minimum alignment for architecture; specify via "
|
||||
"--with-lg-quantum"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define QUANTUM ((size_t)(1U << LG_QUANTUM))
|
||||
#define QUANTUM_MASK (QUANTUM - 1)
|
||||
|
||||
/* Return the smallest quantum multiple that is >= a. */
|
||||
#define QUANTUM_CEILING(a) \
|
||||
(((a) + QUANTUM_MASK) & ~QUANTUM_MASK)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_QUANTUM_H */
|
@ -4,7 +4,7 @@
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/rtree_tsd.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/tsd.h"
|
||||
|
||||
/*
|
||||
@ -31,7 +31,7 @@
|
||||
# error Unsupported number of significant virtual address bits
|
||||
#endif
|
||||
/* Use compact leaf representation if virtual address encoding allows. */
|
||||
#if RTREE_NHIB >= LG_CEIL_NSIZES
|
||||
#if RTREE_NHIB >= LG_CEIL(SC_NSIZES)
|
||||
# define RTREE_LEAF_COMPACT
|
||||
#endif
|
||||
|
||||
@ -170,8 +170,8 @@ rtree_subkey(uintptr_t key, unsigned level) {
|
||||
*/
|
||||
# ifdef RTREE_LEAF_COMPACT
|
||||
JEMALLOC_ALWAYS_INLINE uintptr_t
|
||||
rtree_leaf_elm_bits_read(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,
|
||||
bool dependent) {
|
||||
rtree_leaf_elm_bits_read(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, bool dependent) {
|
||||
return (uintptr_t)atomic_load_p(&elm->le_bits, dependent
|
||||
? ATOMIC_RELAXED : ATOMIC_ACQUIRE);
|
||||
}
|
||||
@ -208,7 +208,7 @@ rtree_leaf_elm_bits_slab_get(uintptr_t bits) {
|
||||
# endif
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE extent_t *
|
||||
rtree_leaf_elm_extent_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
rtree_leaf_elm_extent_read(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, bool dependent) {
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
|
||||
@ -221,7 +221,7 @@ rtree_leaf_elm_extent_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE szind_t
|
||||
rtree_leaf_elm_szind_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
rtree_leaf_elm_szind_read(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, bool dependent) {
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
|
||||
@ -233,7 +233,7 @@ rtree_leaf_elm_szind_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
rtree_leaf_elm_slab_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
rtree_leaf_elm_slab_read(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, bool dependent) {
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
|
||||
@ -245,7 +245,7 @@ rtree_leaf_elm_slab_read(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
}
|
||||
|
||||
static inline void
|
||||
rtree_leaf_elm_extent_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
rtree_leaf_elm_extent_write(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, extent_t *extent) {
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, true);
|
||||
@ -259,9 +259,9 @@ rtree_leaf_elm_extent_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
}
|
||||
|
||||
static inline void
|
||||
rtree_leaf_elm_szind_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
rtree_leaf_elm_szind_write(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, szind_t szind) {
|
||||
assert(szind <= NSIZES);
|
||||
assert(szind <= SC_NSIZES);
|
||||
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,
|
||||
@ -277,7 +277,7 @@ rtree_leaf_elm_szind_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
}
|
||||
|
||||
static inline void
|
||||
rtree_leaf_elm_slab_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
rtree_leaf_elm_slab_write(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, bool slab) {
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,
|
||||
@ -292,8 +292,8 @@ rtree_leaf_elm_slab_write(UNUSED tsdn_t *tsdn, UNUSED rtree_t *rtree,
|
||||
}
|
||||
|
||||
static inline void
|
||||
rtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,
|
||||
extent_t *extent, szind_t szind, bool slab) {
|
||||
rtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, extent_t *extent, szind_t szind, bool slab) {
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t bits = ((uintptr_t)szind << LG_VADDR) |
|
||||
((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1)) |
|
||||
@ -313,7 +313,7 @@ rtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,
|
||||
static inline void
|
||||
rtree_leaf_elm_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree,
|
||||
rtree_leaf_elm_t *elm, szind_t szind, bool slab) {
|
||||
assert(!slab || szind < NBINS);
|
||||
assert(!slab || szind < SC_NBINS);
|
||||
|
||||
/*
|
||||
* The caller implicitly assures that it is the only writer to the szind
|
||||
@ -429,7 +429,7 @@ rtree_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
|
||||
rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
|
||||
dependent);
|
||||
if (!dependent && elm == NULL) {
|
||||
return NSIZES;
|
||||
return SC_NSIZES;
|
||||
}
|
||||
return rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);
|
||||
}
|
||||
@ -452,6 +452,42 @@ rtree_extent_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to read szind_slab from the L1 cache. Returns true on a hit,
|
||||
* and fills in r_szind and r_slab. Otherwise returns false.
|
||||
*
|
||||
* Key is allowed to be NULL in order to save an extra branch on the
|
||||
* fastpath. returns false in this case.
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
rtree_szind_slab_read_fast(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
|
||||
uintptr_t key, szind_t *r_szind, bool *r_slab) {
|
||||
rtree_leaf_elm_t *elm;
|
||||
|
||||
size_t slot = rtree_cache_direct_map(key);
|
||||
uintptr_t leafkey = rtree_leafkey(key);
|
||||
assert(leafkey != RTREE_LEAFKEY_INVALID);
|
||||
|
||||
if (likely(rtree_ctx->cache[slot].leafkey == leafkey)) {
|
||||
rtree_leaf_elm_t *leaf = rtree_ctx->cache[slot].leaf;
|
||||
assert(leaf != NULL);
|
||||
uintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);
|
||||
elm = &leaf[subkey];
|
||||
|
||||
#ifdef RTREE_LEAF_COMPACT
|
||||
uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree,
|
||||
elm, true);
|
||||
*r_szind = rtree_leaf_elm_bits_szind_get(bits);
|
||||
*r_slab = rtree_leaf_elm_bits_slab_get(bits);
|
||||
#else
|
||||
*r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, true);
|
||||
*r_slab = rtree_leaf_elm_slab_read(tsdn, rtree, elm, true);
|
||||
#endif
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
rtree_szind_slab_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
|
||||
uintptr_t key, bool dependent, szind_t *r_szind, bool *r_slab) {
|
||||
@ -474,7 +510,7 @@ rtree_szind_slab_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
|
||||
static inline void
|
||||
rtree_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
|
||||
uintptr_t key, szind_t szind, bool slab) {
|
||||
assert(!slab || szind < NBINS);
|
||||
assert(!slab || szind < SC_NBINS);
|
||||
|
||||
rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);
|
||||
rtree_leaf_elm_szind_slab_update(tsdn, rtree, elm, szind, slab);
|
||||
@ -486,7 +522,7 @@ rtree_clear(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
|
||||
rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);
|
||||
assert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) !=
|
||||
NULL);
|
||||
rtree_leaf_elm_write(tsdn, rtree, elm, NULL, NSIZES, false);
|
||||
rtree_leaf_elm_write(tsdn, rtree, elm, NULL, SC_NSIZES, false);
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_RTREE_H */
|
||||
|
@ -26,7 +26,7 @@
|
||||
* Zero initializer required for tsd initialization only. Proper initialization
|
||||
* done via rtree_ctx_data_init().
|
||||
*/
|
||||
#define RTREE_CTX_ZERO_INITIALIZER {{{0}}, {{0}}}
|
||||
#define RTREE_CTX_ZERO_INITIALIZER {{{0, 0}}, {{0, 0}}}
|
||||
|
||||
|
||||
typedef struct rtree_leaf_elm_s rtree_leaf_elm_t;
|
||||
|
26
contrib/jemalloc/include/jemalloc/internal/safety_check.h
Normal file
26
contrib/jemalloc/include/jemalloc/internal/safety_check.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef JEMALLOC_INTERNAL_SAFETY_CHECK_H
|
||||
#define JEMALLOC_INTERNAL_SAFETY_CHECK_H
|
||||
|
||||
void safety_check_fail(const char *format, ...);
|
||||
/* Can set to NULL for a default. */
|
||||
void safety_check_set_abort(void (*abort_fn)());
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
safety_check_set_redzone(void *ptr, size_t usize, size_t bumped_usize) {
|
||||
assert(usize < bumped_usize);
|
||||
for (size_t i = usize; i < bumped_usize && i < usize + 32; ++i) {
|
||||
*((unsigned char *)ptr + i) = 0xBC;
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
safety_check_verify_redzone(const void *ptr, size_t usize, size_t bumped_usize)
|
||||
{
|
||||
for (size_t i = usize; i < bumped_usize && i < usize + 32; ++i) {
|
||||
if (unlikely(*((unsigned char *)ptr + i) != 0xBC)) {
|
||||
safety_check_fail("Use after free error\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /*JEMALLOC_INTERNAL_SAFETY_CHECK_H */
|
333
contrib/jemalloc/include/jemalloc/internal/sc.h
Normal file
333
contrib/jemalloc/include/jemalloc/internal/sc.h
Normal file
@ -0,0 +1,333 @@
|
||||
#ifndef JEMALLOC_INTERNAL_SC_H
|
||||
#define JEMALLOC_INTERNAL_SC_H
|
||||
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
|
||||
/*
|
||||
* Size class computations:
|
||||
*
|
||||
* These are a little tricky; we'll first start by describing how things
|
||||
* generally work, and then describe some of the details.
|
||||
*
|
||||
* Ignore the first few size classes for a moment. We can then split all the
|
||||
* remaining size classes into groups. The size classes in a group are spaced
|
||||
* such that they cover allocation request sizes in a power-of-2 range. The
|
||||
* power of two is called the base of the group, and the size classes in it
|
||||
* satisfy allocations in the half-open range (base, base * 2]. There are
|
||||
* SC_NGROUP size classes in each group, equally spaced in the range, so that
|
||||
* each one covers allocations for base / SC_NGROUP possible allocation sizes.
|
||||
* We call that value (base / SC_NGROUP) the delta of the group. Each size class
|
||||
* is delta larger than the one before it (including the initial size class in a
|
||||
* group, which is delta larger than base, the largest size class in the
|
||||
* previous group).
|
||||
* To make the math all work out nicely, we require that SC_NGROUP is a power of
|
||||
* two, and define it in terms of SC_LG_NGROUP. We'll often talk in terms of
|
||||
* lg_base and lg_delta. For each of these groups then, we have that
|
||||
* lg_delta == lg_base - SC_LG_NGROUP.
|
||||
* The size classes in a group with a given lg_base and lg_delta (which, recall,
|
||||
* can be computed from lg_base for these groups) are therefore:
|
||||
* base + 1 * delta
|
||||
* which covers allocations in (base, base + 1 * delta]
|
||||
* base + 2 * delta
|
||||
* which covers allocations in (base + 1 * delta, base + 2 * delta].
|
||||
* base + 3 * delta
|
||||
* which covers allocations in (base + 2 * delta, base + 3 * delta].
|
||||
* ...
|
||||
* base + SC_NGROUP * delta ( == 2 * base)
|
||||
* which covers allocations in (base + (SC_NGROUP - 1) * delta, 2 * base].
|
||||
* (Note that currently SC_NGROUP is always 4, so the "..." is empty in
|
||||
* practice.)
|
||||
* Note that the last size class in the group is the next power of two (after
|
||||
* base), so that we've set up the induction correctly for the next group's
|
||||
* selection of delta.
|
||||
*
|
||||
* Now, let's start considering the first few size classes. Two extra constants
|
||||
* come into play here: LG_QUANTUM and SC_LG_TINY_MIN. LG_QUANTUM ensures
|
||||
* correct platform alignment; all objects of size (1 << LG_QUANTUM) or larger
|
||||
* are at least (1 << LG_QUANTUM) aligned; this can be used to ensure that we
|
||||
* never return improperly aligned memory, by making (1 << LG_QUANTUM) equal the
|
||||
* highest required alignment of a platform. For allocation sizes smaller than
|
||||
* (1 << LG_QUANTUM) though, we can be more relaxed (since we don't support
|
||||
* platforms with types with alignment larger than their size). To allow such
|
||||
* allocations (without wasting space unnecessarily), we introduce tiny size
|
||||
* classes; one per power of two, up until we hit the quantum size. There are
|
||||
* therefore LG_QUANTUM - SC_LG_TINY_MIN such size classes.
|
||||
*
|
||||
* Next, we have a size class of size (1 << LG_QUANTUM). This can't be the
|
||||
* start of a group in the sense we described above (covering a power of two
|
||||
* range) since, if we divided into it to pick a value of delta, we'd get a
|
||||
* delta smaller than (1 << LG_QUANTUM) for sizes >= (1 << LG_QUANTUM), which
|
||||
* is against the rules.
|
||||
*
|
||||
* The first base we can divide by SC_NGROUP while still being at least
|
||||
* (1 << LG_QUANTUM) is SC_NGROUP * (1 << LG_QUANTUM). We can get there by
|
||||
* having SC_NGROUP size classes, spaced (1 << LG_QUANTUM) apart. These size
|
||||
* classes are:
|
||||
* 1 * (1 << LG_QUANTUM)
|
||||
* 2 * (1 << LG_QUANTUM)
|
||||
* 3 * (1 << LG_QUANTUM)
|
||||
* ... (although, as above, this "..." is empty in practice)
|
||||
* SC_NGROUP * (1 << LG_QUANTUM).
|
||||
*
|
||||
* There are SC_NGROUP of these size classes, so we can regard it as a sort of
|
||||
* pseudo-group, even though it spans multiple powers of 2, is divided
|
||||
* differently, and both starts and ends on a power of 2 (as opposed to just
|
||||
* ending). SC_NGROUP is itself a power of two, so the first group after the
|
||||
* pseudo-group has the power-of-two base SC_NGROUP * (1 << LG_QUANTUM), for a
|
||||
* lg_base of LG_QUANTUM + SC_LG_NGROUP. We can divide this base into SC_NGROUP
|
||||
* sizes without violating our LG_QUANTUM requirements, so we can safely set
|
||||
* lg_delta = lg_base - SC_LG_GROUP (== LG_QUANTUM).
|
||||
*
|
||||
* So, in order, the size classes are:
|
||||
*
|
||||
* Tiny size classes:
|
||||
* - Count: LG_QUANTUM - SC_LG_TINY_MIN.
|
||||
* - Sizes:
|
||||
* 1 << SC_LG_TINY_MIN
|
||||
* 1 << (SC_LG_TINY_MIN + 1)
|
||||
* 1 << (SC_LG_TINY_MIN + 2)
|
||||
* ...
|
||||
* 1 << (LG_QUANTUM - 1)
|
||||
*
|
||||
* Initial pseudo-group:
|
||||
* - Count: SC_NGROUP
|
||||
* - Sizes:
|
||||
* 1 * (1 << LG_QUANTUM)
|
||||
* 2 * (1 << LG_QUANTUM)
|
||||
* 3 * (1 << LG_QUANTUM)
|
||||
* ...
|
||||
* SC_NGROUP * (1 << LG_QUANTUM)
|
||||
*
|
||||
* Regular group 0:
|
||||
* - Count: SC_NGROUP
|
||||
* - Sizes:
|
||||
* (relative to lg_base of LG_QUANTUM + SC_LG_NGROUP and lg_delta of
|
||||
* lg_base - SC_LG_NGROUP)
|
||||
* (1 << lg_base) + 1 * (1 << lg_delta)
|
||||
* (1 << lg_base) + 2 * (1 << lg_delta)
|
||||
* (1 << lg_base) + 3 * (1 << lg_delta)
|
||||
* ...
|
||||
* (1 << lg_base) + SC_NGROUP * (1 << lg_delta) [ == (1 << (lg_base + 1)) ]
|
||||
*
|
||||
* Regular group 1:
|
||||
* - Count: SC_NGROUP
|
||||
* - Sizes:
|
||||
* (relative to lg_base of LG_QUANTUM + SC_LG_NGROUP + 1 and lg_delta of
|
||||
* lg_base - SC_LG_NGROUP)
|
||||
* (1 << lg_base) + 1 * (1 << lg_delta)
|
||||
* (1 << lg_base) + 2 * (1 << lg_delta)
|
||||
* (1 << lg_base) + 3 * (1 << lg_delta)
|
||||
* ...
|
||||
* (1 << lg_base) + SC_NGROUP * (1 << lg_delta) [ == (1 << (lg_base + 1)) ]
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* Regular group N:
|
||||
* - Count: SC_NGROUP
|
||||
* - Sizes:
|
||||
* (relative to lg_base of LG_QUANTUM + SC_LG_NGROUP + N and lg_delta of
|
||||
* lg_base - SC_LG_NGROUP)
|
||||
* (1 << lg_base) + 1 * (1 << lg_delta)
|
||||
* (1 << lg_base) + 2 * (1 << lg_delta)
|
||||
* (1 << lg_base) + 3 * (1 << lg_delta)
|
||||
* ...
|
||||
* (1 << lg_base) + SC_NGROUP * (1 << lg_delta) [ == (1 << (lg_base + 1)) ]
|
||||
*
|
||||
*
|
||||
* Representation of metadata:
|
||||
* To make the math easy, we'll mostly work in lg quantities. We record lg_base,
|
||||
* lg_delta, and ndelta (i.e. number of deltas above the base) on a
|
||||
* per-size-class basis, and maintain the invariant that, across all size
|
||||
* classes, size == (1 << lg_base) + ndelta * (1 << lg_delta).
|
||||
*
|
||||
* For regular groups (i.e. those with lg_base >= LG_QUANTUM + SC_LG_NGROUP),
|
||||
* lg_delta is lg_base - SC_LG_NGROUP, and ndelta goes from 1 to SC_NGROUP.
|
||||
*
|
||||
* For the initial tiny size classes (if any), lg_base is lg(size class size).
|
||||
* lg_delta is lg_base for the first size class, and lg_base - 1 for all
|
||||
* subsequent ones. ndelta is always 0.
|
||||
*
|
||||
* For the pseudo-group, if there are no tiny size classes, then we set
|
||||
* lg_base == LG_QUANTUM, lg_delta == LG_QUANTUM, and have ndelta range from 0
|
||||
* to SC_NGROUP - 1. (Note that delta == base, so base + (SC_NGROUP - 1) * delta
|
||||
* is just SC_NGROUP * base, or (1 << (SC_LG_NGROUP + LG_QUANTUM)), so we do
|
||||
* indeed get a power of two that way). If there *are* tiny size classes, then
|
||||
* the first size class needs to have lg_delta relative to the largest tiny size
|
||||
* class. We therefore set lg_base == LG_QUANTUM - 1,
|
||||
* lg_delta == LG_QUANTUM - 1, and ndelta == 1, keeping the rest of the
|
||||
* pseudo-group the same.
|
||||
*
|
||||
*
|
||||
* Other terminology:
|
||||
* "Small" size classes mean those that are allocated out of bins, which is the
|
||||
* same as those that are slab allocated.
|
||||
* "Large" size classes are those that are not small. The cutoff for counting as
|
||||
* large is page size * group size.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Size class N + (1 << SC_LG_NGROUP) twice the size of size class N.
|
||||
*/
|
||||
#define SC_LG_NGROUP 2
|
||||
#define SC_LG_TINY_MIN 3
|
||||
|
||||
#if SC_LG_TINY_MIN == 0
|
||||
/* The div module doesn't support division by 1, which this would require. */
|
||||
#error "Unsupported LG_TINY_MIN"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The definitions below are all determined by the above settings and system
|
||||
* characteristics.
|
||||
*/
|
||||
#define SC_NGROUP (1ULL << SC_LG_NGROUP)
|
||||
#define SC_PTR_BITS ((1ULL << LG_SIZEOF_PTR) * 8)
|
||||
#define SC_NTINY (LG_QUANTUM - SC_LG_TINY_MIN)
|
||||
#define SC_LG_TINY_MAXCLASS (LG_QUANTUM > SC_LG_TINY_MIN ? LG_QUANTUM - 1 : -1)
|
||||
#define SC_NPSEUDO SC_NGROUP
|
||||
#define SC_LG_FIRST_REGULAR_BASE (LG_QUANTUM + SC_LG_NGROUP)
|
||||
/*
|
||||
* We cap allocations to be less than 2 ** (ptr_bits - 1), so the highest base
|
||||
* we need is 2 ** (ptr_bits - 2). (This also means that the last group is 1
|
||||
* size class shorter than the others).
|
||||
* We could probably save some space in arenas by capping this at LG_VADDR size.
|
||||
*/
|
||||
#define SC_LG_BASE_MAX (SC_PTR_BITS - 2)
|
||||
#define SC_NREGULAR (SC_NGROUP * \
|
||||
(SC_LG_BASE_MAX - SC_LG_FIRST_REGULAR_BASE + 1) - 1)
|
||||
#define SC_NSIZES (SC_NTINY + SC_NPSEUDO + SC_NREGULAR)
|
||||
|
||||
/* The number of size classes that are a multiple of the page size. */
|
||||
#define SC_NPSIZES ( \
|
||||
/* Start with all the size classes. */ \
|
||||
SC_NSIZES \
|
||||
/* Subtract out those groups with too small a base. */ \
|
||||
- (LG_PAGE - 1 - SC_LG_FIRST_REGULAR_BASE) * SC_NGROUP \
|
||||
/* And the pseudo-group. */ \
|
||||
- SC_NPSEUDO \
|
||||
/* And the tiny group. */ \
|
||||
- SC_NTINY \
|
||||
/* Sizes where ndelta*delta is not a multiple of the page size. */ \
|
||||
- (SC_LG_NGROUP * SC_NGROUP))
|
||||
/*
|
||||
* Note that the last line is computed as the sum of the second column in the
|
||||
* following table:
|
||||
* lg(base) | count of sizes to exclude
|
||||
* ------------------------------|-----------------------------
|
||||
* LG_PAGE - 1 | SC_NGROUP - 1
|
||||
* LG_PAGE | SC_NGROUP - 1
|
||||
* LG_PAGE + 1 | SC_NGROUP - 2
|
||||
* LG_PAGE + 2 | SC_NGROUP - 4
|
||||
* ... | ...
|
||||
* LG_PAGE + (SC_LG_NGROUP - 1) | SC_NGROUP - (SC_NGROUP / 2)
|
||||
*/
|
||||
|
||||
/*
|
||||
* We declare a size class is binnable if size < page size * group. Or, in other
|
||||
* words, lg(size) < lg(page size) + lg(group size).
|
||||
*/
|
||||
#define SC_NBINS ( \
|
||||
/* Sub-regular size classes. */ \
|
||||
SC_NTINY + SC_NPSEUDO \
|
||||
/* Groups with lg_regular_min_base <= lg_base <= lg_base_max */ \
|
||||
+ SC_NGROUP * (LG_PAGE + SC_LG_NGROUP - SC_LG_FIRST_REGULAR_BASE) \
|
||||
/* Last SC of the last group hits the bound exactly; exclude it. */ \
|
||||
- 1)
|
||||
|
||||
/*
|
||||
* The size2index_tab lookup table uses uint8_t to encode each bin index, so we
|
||||
* cannot support more than 256 small size classes.
|
||||
*/
|
||||
#if (SC_NBINS > 256)
|
||||
# error "Too many small size classes"
|
||||
#endif
|
||||
|
||||
/* The largest size class in the lookup table. */
|
||||
#define SC_LOOKUP_MAXCLASS ((size_t)1 << 12)
|
||||
|
||||
/* Internal, only used for the definition of SC_SMALL_MAXCLASS. */
|
||||
#define SC_SMALL_MAX_BASE ((size_t)1 << (LG_PAGE + SC_LG_NGROUP - 1))
|
||||
#define SC_SMALL_MAX_DELTA ((size_t)1 << (LG_PAGE - 1))
|
||||
|
||||
/* The largest size class allocated out of a slab. */
|
||||
#define SC_SMALL_MAXCLASS (SC_SMALL_MAX_BASE \
|
||||
+ (SC_NGROUP - 1) * SC_SMALL_MAX_DELTA)
|
||||
|
||||
/* The smallest size class not allocated out of a slab. */
|
||||
#define SC_LARGE_MINCLASS ((size_t)1ULL << (LG_PAGE + SC_LG_NGROUP))
|
||||
#define SC_LG_LARGE_MINCLASS (LG_PAGE + SC_LG_NGROUP)
|
||||
|
||||
/* Internal; only used for the definition of SC_LARGE_MAXCLASS. */
|
||||
#define SC_MAX_BASE ((size_t)1 << (SC_PTR_BITS - 2))
|
||||
#define SC_MAX_DELTA ((size_t)1 << (SC_PTR_BITS - 2 - SC_LG_NGROUP))
|
||||
|
||||
/* The largest size class supported. */
|
||||
#define SC_LARGE_MAXCLASS (SC_MAX_BASE + (SC_NGROUP - 1) * SC_MAX_DELTA)
|
||||
|
||||
typedef struct sc_s sc_t;
|
||||
struct sc_s {
|
||||
/* Size class index, or -1 if not a valid size class. */
|
||||
int index;
|
||||
/* Lg group base size (no deltas added). */
|
||||
int lg_base;
|
||||
/* Lg delta to previous size class. */
|
||||
int lg_delta;
|
||||
/* Delta multiplier. size == 1<<lg_base + ndelta<<lg_delta */
|
||||
int ndelta;
|
||||
/*
|
||||
* True if the size class is a multiple of the page size, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool psz;
|
||||
/*
|
||||
* True if the size class is a small, bin, size class. False otherwise.
|
||||
*/
|
||||
bool bin;
|
||||
/* The slab page count if a small bin size class, 0 otherwise. */
|
||||
int pgs;
|
||||
/* Same as lg_delta if a lookup table size class, 0 otherwise. */
|
||||
int lg_delta_lookup;
|
||||
};
|
||||
|
||||
typedef struct sc_data_s sc_data_t;
|
||||
struct sc_data_s {
|
||||
/* Number of tiny size classes. */
|
||||
unsigned ntiny;
|
||||
/* Number of bins supported by the lookup table. */
|
||||
int nlbins;
|
||||
/* Number of small size class bins. */
|
||||
int nbins;
|
||||
/* Number of size classes. */
|
||||
int nsizes;
|
||||
/* Number of bits required to store NSIZES. */
|
||||
int lg_ceil_nsizes;
|
||||
/* Number of size classes that are a multiple of (1U << LG_PAGE). */
|
||||
unsigned npsizes;
|
||||
/* Lg of maximum tiny size class (or -1, if none). */
|
||||
int lg_tiny_maxclass;
|
||||
/* Maximum size class included in lookup table. */
|
||||
size_t lookup_maxclass;
|
||||
/* Maximum small size class. */
|
||||
size_t small_maxclass;
|
||||
/* Lg of minimum large size class. */
|
||||
int lg_large_minclass;
|
||||
/* The minimum large size class. */
|
||||
size_t large_minclass;
|
||||
/* Maximum (large) size class. */
|
||||
size_t large_maxclass;
|
||||
/* True if the sc_data_t has been initialized (for debugging only). */
|
||||
bool initialized;
|
||||
|
||||
sc_t sc[SC_NSIZES];
|
||||
};
|
||||
|
||||
void sc_data_init(sc_data_t *data);
|
||||
/*
|
||||
* Updates slab sizes in [begin, end] to be pgs pages in length, if possible.
|
||||
* Otherwise, does its best to accomodate the request.
|
||||
*/
|
||||
void sc_data_update_slab_size(sc_data_t *data, size_t begin, size_t end,
|
||||
int pgs);
|
||||
void sc_boot(sc_data_t *data);
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_SC_H */
|
55
contrib/jemalloc/include/jemalloc/internal/seq.h
Normal file
55
contrib/jemalloc/include/jemalloc/internal/seq.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef JEMALLOC_INTERNAL_SEQ_H
|
||||
#define JEMALLOC_INTERNAL_SEQ_H
|
||||
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
|
||||
/*
|
||||
* A simple seqlock implementation.
|
||||
*/
|
||||
|
||||
#define seq_define(type, short_type) \
|
||||
typedef struct { \
|
||||
atomic_zu_t seq; \
|
||||
atomic_zu_t data[ \
|
||||
(sizeof(type) + sizeof(size_t) - 1) / sizeof(size_t)]; \
|
||||
} seq_##short_type##_t; \
|
||||
\
|
||||
/* \
|
||||
* No internal synchronization -- the caller must ensure that there's \
|
||||
* only a single writer at a time. \
|
||||
*/ \
|
||||
static inline void \
|
||||
seq_store_##short_type(seq_##short_type##_t *dst, type *src) { \
|
||||
size_t buf[sizeof(dst->data) / sizeof(size_t)]; \
|
||||
buf[sizeof(buf) / sizeof(size_t) - 1] = 0; \
|
||||
memcpy(buf, src, sizeof(type)); \
|
||||
size_t old_seq = atomic_load_zu(&dst->seq, ATOMIC_RELAXED); \
|
||||
atomic_store_zu(&dst->seq, old_seq + 1, ATOMIC_RELAXED); \
|
||||
atomic_fence(ATOMIC_RELEASE); \
|
||||
for (size_t i = 0; i < sizeof(buf) / sizeof(size_t); i++) { \
|
||||
atomic_store_zu(&dst->data[i], buf[i], ATOMIC_RELAXED); \
|
||||
} \
|
||||
atomic_store_zu(&dst->seq, old_seq + 2, ATOMIC_RELEASE); \
|
||||
} \
|
||||
\
|
||||
/* Returns whether or not the read was consistent. */ \
|
||||
static inline bool \
|
||||
seq_try_load_##short_type(type *dst, seq_##short_type##_t *src) { \
|
||||
size_t buf[sizeof(src->data) / sizeof(size_t)]; \
|
||||
size_t seq1 = atomic_load_zu(&src->seq, ATOMIC_ACQUIRE); \
|
||||
if (seq1 % 2 != 0) { \
|
||||
return false; \
|
||||
} \
|
||||
for (size_t i = 0; i < sizeof(buf) / sizeof(size_t); i++) { \
|
||||
buf[i] = atomic_load_zu(&src->data[i], ATOMIC_RELAXED); \
|
||||
} \
|
||||
atomic_fence(ATOMIC_ACQUIRE); \
|
||||
size_t seq2 = atomic_load_zu(&src->seq, ATOMIC_RELAXED); \
|
||||
if (seq1 != seq2) { \
|
||||
return false; \
|
||||
} \
|
||||
memcpy(dst, buf, sizeof(type)); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_SEQ_H */
|
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,8 @@
|
||||
OPTION('a', unmerged, config_stats, false) \
|
||||
OPTION('b', bins, true, false) \
|
||||
OPTION('l', large, true, false) \
|
||||
OPTION('x', mutex, true, false)
|
||||
OPTION('x', mutex, true, false) \
|
||||
OPTION('e', extents, true, false)
|
||||
|
||||
enum {
|
||||
#define OPTION(o, v, d, s) stats_print_option_num_##v,
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/pages.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
|
||||
/*
|
||||
@ -26,18 +26,18 @@
|
||||
* sz_pind2sz_tab encodes the same information as could be computed by
|
||||
* sz_pind2sz_compute().
|
||||
*/
|
||||
extern size_t const sz_pind2sz_tab[NPSIZES+1];
|
||||
extern size_t sz_pind2sz_tab[SC_NPSIZES + 1];
|
||||
/*
|
||||
* sz_index2size_tab encodes the same information as could be computed (at
|
||||
* unacceptable cost in some code paths) by sz_index2size_compute().
|
||||
*/
|
||||
extern size_t const sz_index2size_tab[NSIZES];
|
||||
extern size_t sz_index2size_tab[SC_NSIZES];
|
||||
/*
|
||||
* sz_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,
|
||||
* and all accesses are via sz_size2index().
|
||||
*/
|
||||
extern uint8_t const sz_size2index_tab[];
|
||||
extern uint8_t sz_size2index_tab[];
|
||||
|
||||
static const size_t sz_large_pad =
|
||||
#ifdef JEMALLOC_CACHE_OBLIVIOUS
|
||||
@ -47,49 +47,47 @@ static const size_t sz_large_pad =
|
||||
#endif
|
||||
;
|
||||
|
||||
extern void sz_boot(const sc_data_t *sc_data);
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE pszind_t
|
||||
sz_psz2ind(size_t psz) {
|
||||
if (unlikely(psz > LARGE_MAXCLASS)) {
|
||||
return NPSIZES;
|
||||
if (unlikely(psz > SC_LARGE_MAXCLASS)) {
|
||||
return SC_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 x = lg_floor((psz<<1)-1);
|
||||
pszind_t shift = (x < SC_LG_NGROUP + LG_PAGE) ?
|
||||
0 : x - (SC_LG_NGROUP + LG_PAGE);
|
||||
pszind_t grp = shift << SC_LG_NGROUP;
|
||||
|
||||
pszind_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_PAGE + 1) ?
|
||||
LG_PAGE : x - LG_SIZE_CLASS_GROUP - 1;
|
||||
pszind_t lg_delta = (x < SC_LG_NGROUP + LG_PAGE + 1) ?
|
||||
LG_PAGE : x - SC_LG_NGROUP - 1;
|
||||
|
||||
size_t delta_inverse_mask = ZU(-1) << lg_delta;
|
||||
pszind_t mod = ((((psz-1) & delta_inverse_mask) >> lg_delta)) &
|
||||
((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);
|
||||
size_t delta_inverse_mask = ZU(-1) << lg_delta;
|
||||
pszind_t mod = ((((psz-1) & delta_inverse_mask) >> lg_delta)) &
|
||||
((ZU(1) << SC_LG_NGROUP) - 1);
|
||||
|
||||
pszind_t ind = grp + mod;
|
||||
return ind;
|
||||
}
|
||||
pszind_t ind = grp + mod;
|
||||
return ind;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
sz_pind2sz_compute(pszind_t pind) {
|
||||
if (unlikely(pind == NPSIZES)) {
|
||||
return LARGE_MAXCLASS + PAGE;
|
||||
if (unlikely(pind == SC_NPSIZES)) {
|
||||
return SC_LARGE_MAXCLASS + PAGE;
|
||||
}
|
||||
{
|
||||
size_t grp = pind >> LG_SIZE_CLASS_GROUP;
|
||||
size_t mod = pind & ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);
|
||||
size_t grp = pind >> SC_LG_NGROUP;
|
||||
size_t mod = pind & ((ZU(1) << SC_LG_NGROUP) - 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 grp_size_mask = ~((!!grp)-1);
|
||||
size_t grp_size = ((ZU(1) << (LG_PAGE + (SC_LG_NGROUP-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 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;
|
||||
}
|
||||
size_t sz = grp_size + mod_size;
|
||||
return sz;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
@ -101,70 +99,70 @@ sz_pind2sz_lookup(pszind_t pind) {
|
||||
|
||||
static inline size_t
|
||||
sz_pind2sz(pszind_t pind) {
|
||||
assert(pind < NPSIZES+1);
|
||||
assert(pind < SC_NPSIZES + 1);
|
||||
return sz_pind2sz_lookup(pind);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
sz_psz2u(size_t psz) {
|
||||
if (unlikely(psz > LARGE_MAXCLASS)) {
|
||||
return LARGE_MAXCLASS + PAGE;
|
||||
}
|
||||
{
|
||||
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;
|
||||
if (unlikely(psz > SC_LARGE_MAXCLASS)) {
|
||||
return SC_LARGE_MAXCLASS + PAGE;
|
||||
}
|
||||
size_t x = lg_floor((psz<<1)-1);
|
||||
size_t lg_delta = (x < SC_LG_NGROUP + LG_PAGE + 1) ?
|
||||
LG_PAGE : x - SC_LG_NGROUP - 1;
|
||||
size_t delta = ZU(1) << lg_delta;
|
||||
size_t delta_mask = delta - 1;
|
||||
size_t usize = (psz + delta_mask) & ~delta_mask;
|
||||
return usize;
|
||||
}
|
||||
|
||||
static inline szind_t
|
||||
sz_size2index_compute(size_t size) {
|
||||
if (unlikely(size > LARGE_MAXCLASS)) {
|
||||
return NSIZES;
|
||||
if (unlikely(size > SC_LARGE_MAXCLASS)) {
|
||||
return SC_NSIZES;
|
||||
}
|
||||
#if (NTBINS != 0)
|
||||
if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
|
||||
szind_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
#if (SC_NTINY != 0)
|
||||
if (size <= (ZU(1) << SC_LG_TINY_MAXCLASS)) {
|
||||
szind_t lg_tmin = SC_LG_TINY_MAXCLASS - SC_NTINY + 1;
|
||||
szind_t lg_ceil = lg_floor(pow2_ceil_zu(size));
|
||||
return (lg_ceil < lg_tmin ? 0 : lg_ceil - lg_tmin);
|
||||
}
|
||||
#endif
|
||||
{
|
||||
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;
|
||||
szind_t shift = (x < SC_LG_NGROUP + LG_QUANTUM) ? 0 :
|
||||
x - (SC_LG_NGROUP + LG_QUANTUM);
|
||||
szind_t grp = shift << SC_LG_NGROUP;
|
||||
|
||||
szind_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1)
|
||||
? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1;
|
||||
szind_t lg_delta = (x < SC_LG_NGROUP + LG_QUANTUM + 1)
|
||||
? LG_QUANTUM : x - SC_LG_NGROUP - 1;
|
||||
|
||||
size_t delta_inverse_mask = ZU(-1) << lg_delta;
|
||||
szind_t mod = ((((size-1) & delta_inverse_mask) >> lg_delta)) &
|
||||
((ZU(1) << LG_SIZE_CLASS_GROUP) - 1);
|
||||
((ZU(1) << SC_LG_NGROUP) - 1);
|
||||
|
||||
szind_t index = NTBINS + grp + mod;
|
||||
szind_t index = SC_NTINY + grp + mod;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE szind_t
|
||||
sz_size2index_lookup(size_t size) {
|
||||
assert(size <= LOOKUP_MAXCLASS);
|
||||
{
|
||||
szind_t ret = (sz_size2index_tab[(size-1) >> LG_TINY_MIN]);
|
||||
assert(ret == sz_size2index_compute(size));
|
||||
return ret;
|
||||
}
|
||||
assert(size <= SC_LOOKUP_MAXCLASS);
|
||||
szind_t ret = (sz_size2index_tab[(size + (ZU(1) << SC_LG_TINY_MIN) - 1)
|
||||
>> SC_LG_TINY_MIN]);
|
||||
assert(ret == sz_size2index_compute(size));
|
||||
return ret;
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE szind_t
|
||||
sz_size2index(size_t size) {
|
||||
assert(size > 0);
|
||||
if (likely(size <= LOOKUP_MAXCLASS)) {
|
||||
if (likely(size <= SC_LOOKUP_MAXCLASS)) {
|
||||
return sz_size2index_lookup(size);
|
||||
}
|
||||
return sz_size2index_compute(size);
|
||||
@ -172,20 +170,20 @@ sz_size2index(size_t size) {
|
||||
|
||||
static inline size_t
|
||||
sz_index2size_compute(szind_t index) {
|
||||
#if (NTBINS > 0)
|
||||
if (index < NTBINS) {
|
||||
return (ZU(1) << (LG_TINY_MAXCLASS - NTBINS + 1 + index));
|
||||
#if (SC_NTINY > 0)
|
||||
if (index < SC_NTINY) {
|
||||
return (ZU(1) << (SC_LG_TINY_MAXCLASS - SC_NTINY + 1 + index));
|
||||
}
|
||||
#endif
|
||||
{
|
||||
size_t reduced_index = index - NTBINS;
|
||||
size_t grp = reduced_index >> LG_SIZE_CLASS_GROUP;
|
||||
size_t mod = reduced_index & ((ZU(1) << LG_SIZE_CLASS_GROUP) -
|
||||
size_t reduced_index = index - SC_NTINY;
|
||||
size_t grp = reduced_index >> SC_LG_NGROUP;
|
||||
size_t mod = reduced_index & ((ZU(1) << SC_LG_NGROUP) -
|
||||
1);
|
||||
|
||||
size_t grp_size_mask = ~((!!grp)-1);
|
||||
size_t grp_size = ((ZU(1) << (LG_QUANTUM +
|
||||
(LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask;
|
||||
(SC_LG_NGROUP-1))) << grp) & grp_size_mask;
|
||||
|
||||
size_t shift = (grp == 0) ? 1 : grp;
|
||||
size_t lg_delta = shift + (LG_QUANTUM-1);
|
||||
@ -205,18 +203,22 @@ sz_index2size_lookup(szind_t index) {
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
sz_index2size(szind_t index) {
|
||||
assert(index < NSIZES);
|
||||
assert(index < SC_NSIZES);
|
||||
return sz_index2size_lookup(index);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
sz_s2u_compute(size_t size) {
|
||||
if (unlikely(size > LARGE_MAXCLASS)) {
|
||||
if (unlikely(size > SC_LARGE_MAXCLASS)) {
|
||||
return 0;
|
||||
}
|
||||
#if (NTBINS > 0)
|
||||
if (size <= (ZU(1) << LG_TINY_MAXCLASS)) {
|
||||
size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1;
|
||||
|
||||
if (size == 0) {
|
||||
size++;
|
||||
}
|
||||
#if (SC_NTINY > 0)
|
||||
if (size <= (ZU(1) << SC_LG_TINY_MAXCLASS)) {
|
||||
size_t lg_tmin = SC_LG_TINY_MAXCLASS - SC_NTINY + 1;
|
||||
size_t lg_ceil = lg_floor(pow2_ceil_zu(size));
|
||||
return (lg_ceil < lg_tmin ? (ZU(1) << lg_tmin) :
|
||||
(ZU(1) << lg_ceil));
|
||||
@ -224,8 +226,8 @@ sz_s2u_compute(size_t size) {
|
||||
#endif
|
||||
{
|
||||
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 lg_delta = (x < SC_LG_NGROUP + LG_QUANTUM + 1)
|
||||
? LG_QUANTUM : x - SC_LG_NGROUP - 1;
|
||||
size_t delta = ZU(1) << lg_delta;
|
||||
size_t delta_mask = delta - 1;
|
||||
size_t usize = (size + delta_mask) & ~delta_mask;
|
||||
@ -247,8 +249,7 @@ sz_s2u_lookup(size_t size) {
|
||||
*/
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
sz_s2u(size_t size) {
|
||||
assert(size > 0);
|
||||
if (likely(size <= LOOKUP_MAXCLASS)) {
|
||||
if (likely(size <= SC_LOOKUP_MAXCLASS)) {
|
||||
return sz_s2u_lookup(size);
|
||||
}
|
||||
return sz_s2u_compute(size);
|
||||
@ -265,7 +266,7 @@ sz_sa2u(size_t size, size_t alignment) {
|
||||
assert(alignment != 0 && ((alignment - 1) & alignment) == 0);
|
||||
|
||||
/* Try for a small size class. */
|
||||
if (size <= SMALL_MAXCLASS && alignment < PAGE) {
|
||||
if (size <= SC_SMALL_MAXCLASS && alignment < PAGE) {
|
||||
/*
|
||||
* Round size up to the nearest multiple of alignment.
|
||||
*
|
||||
@ -281,20 +282,20 @@ sz_sa2u(size_t size, size_t alignment) {
|
||||
* 192 | 11000000 | 64
|
||||
*/
|
||||
usize = sz_s2u(ALIGNMENT_CEILING(size, alignment));
|
||||
if (usize < LARGE_MINCLASS) {
|
||||
if (usize < SC_LARGE_MINCLASS) {
|
||||
return usize;
|
||||
}
|
||||
}
|
||||
|
||||
/* Large size class. Beware of overflow. */
|
||||
|
||||
if (unlikely(alignment > LARGE_MAXCLASS)) {
|
||||
if (unlikely(alignment > SC_LARGE_MAXCLASS)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure result is a large size class. */
|
||||
if (size <= LARGE_MINCLASS) {
|
||||
usize = LARGE_MINCLASS;
|
||||
if (size <= SC_LARGE_MINCLASS) {
|
||||
usize = SC_LARGE_MINCLASS;
|
||||
} else {
|
||||
usize = sz_s2u(size);
|
||||
if (usize < size) {
|
||||
|
@ -1,15 +1,13 @@
|
||||
#ifndef JEMALLOC_INTERNAL_TCACHE_EXTERNS_H
|
||||
#define JEMALLOC_INTERNAL_TCACHE_EXTERNS_H
|
||||
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
|
||||
extern bool opt_tcache;
|
||||
extern ssize_t opt_lg_tcache_max;
|
||||
|
||||
extern cache_bin_info_t *tcache_bin_info;
|
||||
|
||||
/*
|
||||
* Number of tcache bins. There are NBINS small-object bins, plus 0 or more
|
||||
* Number of tcache bins. There are SC_NBINS small-object bins, plus 0 or more
|
||||
* large-object bins.
|
||||
*/
|
||||
extern unsigned nhbins;
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "jemalloc/internal/bin.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/sz.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
@ -40,13 +40,13 @@ tcache_event(tsd_t *tsd, tcache_t *tcache) {
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
|
||||
UNUSED size_t size, szind_t binind, bool zero, bool slow_path) {
|
||||
size_t size, szind_t binind, bool zero, bool slow_path) {
|
||||
void *ret;
|
||||
cache_bin_t *bin;
|
||||
bool tcache_success;
|
||||
size_t usize JEMALLOC_CC_SILENCE_INIT(0);
|
||||
|
||||
assert(binind < NBINS);
|
||||
assert(binind < SC_NBINS);
|
||||
bin = tcache_small_bin_get(tcache, binind);
|
||||
ret = cache_bin_alloc_easy(bin, &tcache_success);
|
||||
assert(tcache_success == (ret != NULL));
|
||||
@ -107,7 +107,7 @@ tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
|
||||
cache_bin_t *bin;
|
||||
bool tcache_success;
|
||||
|
||||
assert(binind >= NBINS &&binind < nhbins);
|
||||
assert(binind >= SC_NBINS &&binind < nhbins);
|
||||
bin = tcache_large_bin_get(tcache, binind);
|
||||
ret = cache_bin_alloc_easy(bin, &tcache_success);
|
||||
assert(tcache_success == (ret != NULL));
|
||||
@ -166,7 +166,8 @@ tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
|
||||
cache_bin_t *bin;
|
||||
cache_bin_info_t *bin_info;
|
||||
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= SMALL_MAXCLASS);
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr)
|
||||
<= SC_SMALL_MAXCLASS);
|
||||
|
||||
if (slow_path && config_fill && unlikely(opt_junk_free)) {
|
||||
arena_dalloc_junk_small(ptr, &bin_infos[binind]);
|
||||
@ -174,13 +175,12 @@ tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
|
||||
|
||||
bin = tcache_small_bin_get(tcache, binind);
|
||||
bin_info = &tcache_bin_info[binind];
|
||||
if (unlikely(bin->ncached == bin_info->ncached_max)) {
|
||||
if (unlikely(!cache_bin_dalloc_easy(bin, bin_info, ptr))) {
|
||||
tcache_bin_flush_small(tsd, tcache, bin, binind,
|
||||
(bin_info->ncached_max >> 1));
|
||||
bool ret = cache_bin_dalloc_easy(bin, bin_info, ptr);
|
||||
assert(ret);
|
||||
}
|
||||
assert(bin->ncached < bin_info->ncached_max);
|
||||
bin->ncached++;
|
||||
*(bin->avail - bin->ncached) = ptr;
|
||||
|
||||
tcache_event(tsd, tcache);
|
||||
}
|
||||
@ -191,7 +191,8 @@ tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
|
||||
cache_bin_t *bin;
|
||||
cache_bin_info_t *bin_info;
|
||||
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr) > SMALL_MAXCLASS);
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr)
|
||||
> SC_SMALL_MAXCLASS);
|
||||
assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= tcache_maxclass);
|
||||
|
||||
if (slow_path && config_fill && unlikely(opt_junk_free)) {
|
||||
@ -215,6 +216,9 @@ JEMALLOC_ALWAYS_INLINE tcache_t *
|
||||
tcaches_get(tsd_t *tsd, unsigned ind) {
|
||||
tcaches_t *elm = &tcaches[ind];
|
||||
if (unlikely(elm->tcache == NULL)) {
|
||||
malloc_printf("<jemalloc>: invalid tcache id (%u).\n", ind);
|
||||
abort();
|
||||
} else if (unlikely(elm->tcache == TCACHES_ELM_NEED_REINIT)) {
|
||||
elm->tcache = tcache_create_explicit(tsd);
|
||||
}
|
||||
return elm->tcache;
|
||||
|
@ -1,10 +1,14 @@
|
||||
#ifndef JEMALLOC_INTERNAL_TCACHE_STRUCTS_H
|
||||
#define JEMALLOC_INTERNAL_TCACHE_STRUCTS_H
|
||||
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/cache_bin.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/ticker.h"
|
||||
#include "jemalloc/internal/tsd_types.h"
|
||||
|
||||
/* Various uses of this struct need it to be a named type. */
|
||||
typedef ql_elm(tsd_t) tsd_link_t;
|
||||
|
||||
struct tcache_s {
|
||||
/*
|
||||
@ -21,7 +25,7 @@ struct tcache_s {
|
||||
* During tcache initialization, the avail pointer in each element of
|
||||
* tbins is initialized to point to the proper offset within this array.
|
||||
*/
|
||||
cache_bin_t bins_small[NBINS];
|
||||
cache_bin_t bins_small[SC_NBINS];
|
||||
|
||||
/*
|
||||
* This data is less hot; we can be a little less careful with our
|
||||
@ -29,6 +33,11 @@ struct tcache_s {
|
||||
*/
|
||||
/* Lets us track all the tcaches in an arena. */
|
||||
ql_elm(tcache_t) link;
|
||||
|
||||
/* Logically scoped to tsd, but put here for cache layout reasons. */
|
||||
ql_elm(tsd_t) tsd_link;
|
||||
bool in_hook;
|
||||
|
||||
/*
|
||||
* The descriptor lets the arena find our cache bins without seeing the
|
||||
* tcache definition. This enables arenas to aggregate stats across
|
||||
@ -41,13 +50,13 @@ struct tcache_s {
|
||||
/* Next bin to GC. */
|
||||
szind_t next_gc_bin;
|
||||
/* For small bins, fill (ncached_max >> lg_fill_div). */
|
||||
uint8_t lg_fill_div[NBINS];
|
||||
uint8_t lg_fill_div[SC_NBINS];
|
||||
/*
|
||||
* We put the cache bins for large size classes at the end of the
|
||||
* struct, since some of them might not get used. This might end up
|
||||
* letting us avoid touching an extra page if we don't have to.
|
||||
*/
|
||||
cache_bin_t bins_large[NSIZES-NBINS];
|
||||
cache_bin_t bins_large[SC_NSIZES-SC_NBINS];
|
||||
};
|
||||
|
||||
/* Linkage for list of available (previously used) explicit tcache IDs. */
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef JEMALLOC_INTERNAL_TCACHE_TYPES_H
|
||||
#define JEMALLOC_INTERNAL_TCACHE_TYPES_H
|
||||
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
typedef struct tcache_s tcache_t;
|
||||
typedef struct tcaches_s tcaches_t;
|
||||
@ -45,7 +45,7 @@ typedef struct tcaches_s tcaches_t;
|
||||
|
||||
/* Number of tcache allocation/deallocation events between incremental GCs. */
|
||||
#define TCACHE_GC_INCR \
|
||||
((TCACHE_GC_SWEEP / NBINS) + ((TCACHE_GC_SWEEP / NBINS == 0) ? 0 : 1))
|
||||
((TCACHE_GC_SWEEP / SC_NBINS) + ((TCACHE_GC_SWEEP / SC_NBINS == 0) ? 0 : 1))
|
||||
|
||||
/* Used in TSD static initializer only. Real init in tcache_data_init(). */
|
||||
#define TCACHE_ZERO_INITIALIZER {0}
|
||||
@ -53,4 +53,7 @@ typedef struct tcaches_s tcaches_t;
|
||||
/* Used in TSD static initializer only. Will be initialized to opt_tcache. */
|
||||
#define TCACHE_ENABLED_ZERO_INITIALIZER false
|
||||
|
||||
/* Used for explicit tcache only. Means flushed but not destroyed. */
|
||||
#define TCACHES_ELM_NEED_REINIT ((tcache_t *)(uintptr_t)1)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_TCACHE_TYPES_H */
|
||||
|
12
contrib/jemalloc/include/jemalloc/internal/test_hooks.h
Normal file
12
contrib/jemalloc/include/jemalloc/internal/test_hooks.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef JEMALLOC_INTERNAL_TEST_HOOKS_H
|
||||
#define JEMALLOC_INTERNAL_TEST_HOOKS_H
|
||||
|
||||
extern JEMALLOC_EXPORT void (*test_hooks_arena_new_hook)();
|
||||
extern JEMALLOC_EXPORT void (*test_hooks_libc_hook)();
|
||||
|
||||
#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
|
||||
|
||||
/* Note that this is undef'd and re-define'd in src/prof.c. */
|
||||
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_TEST_HOOKS_H */
|
@ -75,4 +75,17 @@ ticker_tick(ticker_t *ticker) {
|
||||
return ticker_ticks(ticker, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to tick. If ticker would fire, return true, but rely on
|
||||
* slowpath to reset ticker.
|
||||
*/
|
||||
static inline bool
|
||||
ticker_trytick(ticker_t *ticker) {
|
||||
--ticker->tick;
|
||||
if (unlikely(ticker->tick < 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_TICKER_H */
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "jemalloc/internal/arena_types.h"
|
||||
#include "jemalloc/internal/assert.h"
|
||||
#include "jemalloc/internal/bin_types.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_externs.h"
|
||||
#include "jemalloc/internal/prof_types.h"
|
||||
#include "jemalloc/internal/ql.h"
|
||||
@ -68,17 +69,19 @@ typedef void (*test_callback_t)(int *);
|
||||
O(offset_state, uint64_t, uint64_t) \
|
||||
O(thread_allocated, uint64_t, uint64_t) \
|
||||
O(thread_deallocated, uint64_t, uint64_t) \
|
||||
O(bytes_until_sample, int64_t, int64_t) \
|
||||
O(prof_tdata, prof_tdata_t *, prof_tdata_t *) \
|
||||
O(rtree_ctx, rtree_ctx_t, rtree_ctx_t) \
|
||||
O(iarena, arena_t *, arena_t *) \
|
||||
O(arena, arena_t *, arena_t *) \
|
||||
O(arenas_tdata, arena_tdata_t *, arena_tdata_t *)\
|
||||
O(binshards, tsd_binshards_t, tsd_binshards_t)\
|
||||
O(tcache, tcache_t, tcache_t) \
|
||||
O(witness_tsd, witness_tsd_t, witness_tsdn_t) \
|
||||
MALLOC_TEST_TSD
|
||||
|
||||
#define TSD_INITIALIZER { \
|
||||
tsd_state_uninitialized, \
|
||||
ATOMIC_INIT(tsd_state_uninitialized), \
|
||||
TCACHE_ENABLED_ZERO_INITIALIZER, \
|
||||
false, \
|
||||
0, \
|
||||
@ -86,29 +89,97 @@ typedef void (*test_callback_t)(int *);
|
||||
0, \
|
||||
0, \
|
||||
0, \
|
||||
0, \
|
||||
NULL, \
|
||||
RTREE_CTX_ZERO_INITIALIZER, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
TSD_BINSHARDS_ZERO_INITIALIZER, \
|
||||
TCACHE_ZERO_INITIALIZER, \
|
||||
WITNESS_TSD_INITIALIZER \
|
||||
MALLOC_TEST_TSD_INITIALIZER \
|
||||
}
|
||||
|
||||
void *malloc_tsd_malloc(size_t size);
|
||||
void malloc_tsd_dalloc(void *wrapper);
|
||||
void malloc_tsd_cleanup_register(bool (*f)(void));
|
||||
tsd_t *malloc_tsd_boot0(void);
|
||||
void malloc_tsd_boot1(void);
|
||||
void tsd_cleanup(void *arg);
|
||||
tsd_t *tsd_fetch_slow(tsd_t *tsd, bool internal);
|
||||
void tsd_state_set(tsd_t *tsd, uint8_t new_state);
|
||||
void tsd_slow_update(tsd_t *tsd);
|
||||
void tsd_prefork(tsd_t *tsd);
|
||||
void tsd_postfork_parent(tsd_t *tsd);
|
||||
void tsd_postfork_child(tsd_t *tsd);
|
||||
|
||||
/*
|
||||
* Call ..._inc when your module wants to take all threads down the slow paths,
|
||||
* and ..._dec when it no longer needs to.
|
||||
*/
|
||||
void tsd_global_slow_inc(tsdn_t *tsdn);
|
||||
void tsd_global_slow_dec(tsdn_t *tsdn);
|
||||
bool tsd_global_slow();
|
||||
|
||||
enum {
|
||||
tsd_state_nominal = 0, /* Common case --> jnz. */
|
||||
tsd_state_nominal_slow = 1, /* Initialized but on slow path. */
|
||||
/* the above 2 nominal states should be lower values. */
|
||||
tsd_state_nominal_max = 1, /* used for comparison only. */
|
||||
tsd_state_minimal_initialized = 2,
|
||||
tsd_state_purgatory = 3,
|
||||
tsd_state_reincarnated = 4,
|
||||
tsd_state_uninitialized = 5
|
||||
/* Common case --> jnz. */
|
||||
tsd_state_nominal = 0,
|
||||
/* Initialized but on slow path. */
|
||||
tsd_state_nominal_slow = 1,
|
||||
/*
|
||||
* Some thread has changed global state in such a way that all nominal
|
||||
* threads need to recompute their fast / slow status the next time they
|
||||
* get a chance.
|
||||
*
|
||||
* Any thread can change another thread's status *to* recompute, but
|
||||
* threads are the only ones who can change their status *from*
|
||||
* recompute.
|
||||
*/
|
||||
tsd_state_nominal_recompute = 2,
|
||||
/*
|
||||
* The above nominal states should be lower values. We use
|
||||
* tsd_nominal_max to separate nominal states from threads in the
|
||||
* process of being born / dying.
|
||||
*/
|
||||
tsd_state_nominal_max = 2,
|
||||
|
||||
/*
|
||||
* A thread might free() during its death as its only allocator action;
|
||||
* in such scenarios, we need tsd, but set up in such a way that no
|
||||
* cleanup is necessary.
|
||||
*/
|
||||
tsd_state_minimal_initialized = 3,
|
||||
/* States during which we know we're in thread death. */
|
||||
tsd_state_purgatory = 4,
|
||||
tsd_state_reincarnated = 5,
|
||||
/*
|
||||
* What it says on the tin; tsd that hasn't been initialized. Note
|
||||
* that even when the tsd struct lives in TLS, when need to keep track
|
||||
* of stuff like whether or not our pthread destructors have been
|
||||
* scheduled, so this really truly is different than the nominal state.
|
||||
*/
|
||||
tsd_state_uninitialized = 6
|
||||
};
|
||||
|
||||
/* Manually limit tsd_state_t to a single byte. */
|
||||
typedef uint8_t tsd_state_t;
|
||||
/*
|
||||
* Some TSD accesses can only be done in a nominal state. To enforce this, we
|
||||
* wrap TSD member access in a function that asserts on TSD state, and mangle
|
||||
* field names to prevent touching them accidentally.
|
||||
*/
|
||||
#define TSD_MANGLE(n) cant_access_tsd_items_directly_use_a_getter_or_setter_##n
|
||||
|
||||
#ifdef JEMALLOC_U8_ATOMICS
|
||||
# define tsd_state_t atomic_u8_t
|
||||
# define tsd_atomic_load atomic_load_u8
|
||||
# define tsd_atomic_store atomic_store_u8
|
||||
# define tsd_atomic_exchange atomic_exchange_u8
|
||||
#else
|
||||
# define tsd_state_t atomic_u32_t
|
||||
# define tsd_atomic_load atomic_load_u32
|
||||
# define tsd_atomic_store atomic_store_u32
|
||||
# define tsd_atomic_exchange atomic_exchange_u32
|
||||
#endif
|
||||
|
||||
/* The actual tsd. */
|
||||
struct tsd_s {
|
||||
@ -117,14 +188,30 @@ struct tsd_s {
|
||||
* module. Access any thread-local state through the getters and
|
||||
* setters below.
|
||||
*/
|
||||
tsd_state_t state;
|
||||
|
||||
/*
|
||||
* We manually limit the state to just a single byte. Unless the 8-bit
|
||||
* atomics are unavailable (which is rare).
|
||||
*/
|
||||
tsd_state_t state;
|
||||
#define O(n, t, nt) \
|
||||
t use_a_getter_or_setter_instead_##n;
|
||||
t TSD_MANGLE(n);
|
||||
MALLOC_TSD
|
||||
#undef O
|
||||
/* AddressSanitizer requires TLS data to be aligned to at least 8 bytes. */
|
||||
} JEMALLOC_ALIGNED(16);
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE uint8_t
|
||||
tsd_state_get(tsd_t *tsd) {
|
||||
/*
|
||||
* This should be atomic. Unfortunately, compilers right now can't tell
|
||||
* that this can be done as a memory comparison, and forces a load into
|
||||
* a register that hurts fast-path performance.
|
||||
*/
|
||||
/* return atomic_load_u8(&tsd->state, ATOMIC_RELAXED); */
|
||||
return *(uint8_t *)&tsd->state;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -151,15 +238,6 @@ tsdn_tsd(tsdn_t *tsdn) {
|
||||
return &tsdn->tsd;
|
||||
}
|
||||
|
||||
void *malloc_tsd_malloc(size_t size);
|
||||
void malloc_tsd_dalloc(void *wrapper);
|
||||
void malloc_tsd_cleanup_register(bool (*f)(void));
|
||||
tsd_t *malloc_tsd_boot0(void);
|
||||
void malloc_tsd_boot1(void);
|
||||
void tsd_cleanup(void *arg);
|
||||
tsd_t *tsd_fetch_slow(tsd_t *tsd, bool internal);
|
||||
void tsd_slow_update(tsd_t *tsd);
|
||||
|
||||
/*
|
||||
* We put the platform-specific data declarations and inlines into their own
|
||||
* header files to avoid cluttering this file. They define tsd_boot0,
|
||||
@ -183,7 +261,7 @@ void tsd_slow_update(tsd_t *tsd);
|
||||
#define O(n, t, nt) \
|
||||
JEMALLOC_ALWAYS_INLINE t * \
|
||||
tsd_##n##p_get_unsafe(tsd_t *tsd) { \
|
||||
return &tsd->use_a_getter_or_setter_instead_##n; \
|
||||
return &tsd->TSD_MANGLE(n); \
|
||||
}
|
||||
MALLOC_TSD
|
||||
#undef O
|
||||
@ -192,10 +270,16 @@ MALLOC_TSD
|
||||
#define O(n, t, nt) \
|
||||
JEMALLOC_ALWAYS_INLINE t * \
|
||||
tsd_##n##p_get(tsd_t *tsd) { \
|
||||
assert(tsd->state == tsd_state_nominal || \
|
||||
tsd->state == tsd_state_nominal_slow || \
|
||||
tsd->state == tsd_state_reincarnated || \
|
||||
tsd->state == tsd_state_minimal_initialized); \
|
||||
/* \
|
||||
* Because the state might change asynchronously if it's \
|
||||
* nominal, we need to make sure that we only read it once. \
|
||||
*/ \
|
||||
uint8_t state = tsd_state_get(tsd); \
|
||||
assert(state == tsd_state_nominal || \
|
||||
state == tsd_state_nominal_slow || \
|
||||
state == tsd_state_nominal_recompute || \
|
||||
state == tsd_state_reincarnated || \
|
||||
state == tsd_state_minimal_initialized); \
|
||||
return tsd_##n##p_get_unsafe(tsd); \
|
||||
}
|
||||
MALLOC_TSD
|
||||
@ -230,8 +314,8 @@ MALLOC_TSD
|
||||
#define O(n, t, nt) \
|
||||
JEMALLOC_ALWAYS_INLINE void \
|
||||
tsd_##n##_set(tsd_t *tsd, t val) { \
|
||||
assert(tsd->state != tsd_state_reincarnated && \
|
||||
tsd->state != tsd_state_minimal_initialized); \
|
||||
assert(tsd_state_get(tsd) != tsd_state_reincarnated && \
|
||||
tsd_state_get(tsd) != tsd_state_minimal_initialized); \
|
||||
*tsd_##n##p_get(tsd) = val; \
|
||||
}
|
||||
MALLOC_TSD
|
||||
@ -239,13 +323,18 @@ MALLOC_TSD
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
tsd_assert_fast(tsd_t *tsd) {
|
||||
/*
|
||||
* Note that our fastness assertion does *not* include global slowness
|
||||
* counters; it's not in general possible to ensure that they won't
|
||||
* change asynchronously from underneath us.
|
||||
*/
|
||||
assert(!malloc_slow && tsd_tcache_enabled_get(tsd) &&
|
||||
tsd_reentrancy_level_get(tsd) == 0);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE bool
|
||||
tsd_fast(tsd_t *tsd) {
|
||||
bool fast = (tsd->state == tsd_state_nominal);
|
||||
bool fast = (tsd_state_get(tsd) == tsd_state_nominal);
|
||||
if (fast) {
|
||||
tsd_assert_fast(tsd);
|
||||
}
|
||||
@ -262,7 +351,7 @@ tsd_fetch_impl(bool init, bool minimal) {
|
||||
}
|
||||
assert(tsd != NULL);
|
||||
|
||||
if (unlikely(tsd->state != tsd_state_nominal)) {
|
||||
if (unlikely(tsd_state_get(tsd) != tsd_state_nominal)) {
|
||||
return tsd_fetch_slow(tsd, minimal);
|
||||
}
|
||||
assert(tsd_fast(tsd));
|
||||
@ -282,7 +371,7 @@ JEMALLOC_ALWAYS_INLINE tsd_t *
|
||||
tsd_internal_fetch(void) {
|
||||
tsd_t *tsd = tsd_fetch_min();
|
||||
/* Use reincarnated state to prevent full initialization. */
|
||||
tsd->state = tsd_state_reincarnated;
|
||||
tsd_state_set(tsd, tsd_state_reincarnated);
|
||||
|
||||
return tsd;
|
||||
}
|
||||
@ -294,7 +383,7 @@ tsd_fetch(void) {
|
||||
|
||||
static inline bool
|
||||
tsd_nominal(tsd_t *tsd) {
|
||||
return (tsd->state <= tsd_state_nominal_max);
|
||||
return (tsd_state_get(tsd) <= tsd_state_nominal_max);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE tsdn_t *
|
||||
|
@ -77,7 +77,10 @@ tsd_wrapper_get(bool init) {
|
||||
abort();
|
||||
} else {
|
||||
wrapper->initialized = false;
|
||||
JEMALLOC_DIAGNOSTIC_PUSH
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
tsd_t initializer = TSD_INITIALIZER;
|
||||
JEMALLOC_DIAGNOSTIC_POP
|
||||
wrapper->val = initializer;
|
||||
}
|
||||
tsd_wrapper_set(wrapper);
|
||||
@ -107,7 +110,10 @@ tsd_boot1(void) {
|
||||
tsd_boot_wrapper.initialized = false;
|
||||
tsd_cleanup(&tsd_boot_wrapper.val);
|
||||
wrapper->initialized = false;
|
||||
JEMALLOC_DIAGNOSTIC_PUSH
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
tsd_t initializer = TSD_INITIALIZER;
|
||||
JEMALLOC_DIAGNOSTIC_POP
|
||||
wrapper->val = initializer;
|
||||
tsd_wrapper_set(wrapper);
|
||||
}
|
||||
|
@ -3,8 +3,10 @@
|
||||
#endif
|
||||
#define JEMALLOC_INTERNAL_TSD_MALLOC_THREAD_CLEANUP_H
|
||||
|
||||
extern __thread tsd_t tsd_tls;
|
||||
extern __thread bool tsd_initialized;
|
||||
#define JEMALLOC_TSD_TYPE_ATTR(type) __thread type JEMALLOC_TLS_MODEL
|
||||
|
||||
extern JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls;
|
||||
extern JEMALLOC_TSD_TYPE_ATTR(bool) tsd_initialized;
|
||||
extern bool tsd_booted;
|
||||
|
||||
/* Initialization/cleanup. */
|
||||
@ -47,7 +49,6 @@ tsd_get_allocates(void) {
|
||||
/* Get/set. */
|
||||
JEMALLOC_ALWAYS_INLINE tsd_t *
|
||||
tsd_get(bool init) {
|
||||
assert(tsd_booted);
|
||||
return &tsd_tls;
|
||||
}
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
|
@ -3,7 +3,9 @@
|
||||
#endif
|
||||
#define JEMALLOC_INTERNAL_TSD_TLS_H
|
||||
|
||||
extern __thread tsd_t tsd_tls;
|
||||
#define JEMALLOC_TSD_TYPE_ATTR(type) __thread type JEMALLOC_TLS_MODEL
|
||||
|
||||
extern JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls;
|
||||
extern pthread_key_t tsd_tsd;
|
||||
extern bool tsd_booted;
|
||||
|
||||
@ -39,8 +41,7 @@ tsd_get_allocates(void) {
|
||||
|
||||
/* Get/set. */
|
||||
JEMALLOC_ALWAYS_INLINE tsd_t *
|
||||
tsd_get(UNUSED bool init) {
|
||||
assert(tsd_booted);
|
||||
tsd_get(bool init) {
|
||||
return &tsd_tls;
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@
|
||||
#define WITNESS_RANK_PROF_BT2GCTX 6U
|
||||
#define WITNESS_RANK_PROF_TDATAS 7U
|
||||
#define WITNESS_RANK_PROF_TDATA 8U
|
||||
#define WITNESS_RANK_PROF_GCTX 9U
|
||||
|
||||
#define WITNESS_RANK_BACKGROUND_THREAD 10U
|
||||
#define WITNESS_RANK_PROF_LOG 9U
|
||||
#define WITNESS_RANK_PROF_GCTX 10U
|
||||
#define WITNESS_RANK_BACKGROUND_THREAD 11U
|
||||
|
||||
/*
|
||||
* Used as an argument to witness_assert_depth_to_rank() in order to validate
|
||||
@ -37,18 +37,19 @@
|
||||
* witness_assert_depth_to_rank() is inclusive rather than exclusive, this
|
||||
* definition can have the same value as the minimally ranked core lock.
|
||||
*/
|
||||
#define WITNESS_RANK_CORE 11U
|
||||
#define WITNESS_RANK_CORE 12U
|
||||
|
||||
#define WITNESS_RANK_DECAY 11U
|
||||
#define WITNESS_RANK_TCACHE_QL 12U
|
||||
#define WITNESS_RANK_EXTENT_GROW 13U
|
||||
#define WITNESS_RANK_EXTENTS 14U
|
||||
#define WITNESS_RANK_EXTENT_AVAIL 15U
|
||||
#define WITNESS_RANK_DECAY 12U
|
||||
#define WITNESS_RANK_TCACHE_QL 13U
|
||||
#define WITNESS_RANK_EXTENT_GROW 14U
|
||||
#define WITNESS_RANK_EXTENTS 15U
|
||||
#define WITNESS_RANK_EXTENT_AVAIL 16U
|
||||
|
||||
#define WITNESS_RANK_EXTENT_POOL 16U
|
||||
#define WITNESS_RANK_RTREE 17U
|
||||
#define WITNESS_RANK_BASE 18U
|
||||
#define WITNESS_RANK_ARENA_LARGE 19U
|
||||
#define WITNESS_RANK_EXTENT_POOL 17U
|
||||
#define WITNESS_RANK_RTREE 18U
|
||||
#define WITNESS_RANK_BASE 19U
|
||||
#define WITNESS_RANK_ARENA_LARGE 20U
|
||||
#define WITNESS_RANK_HOOK 21U
|
||||
|
||||
#define WITNESS_RANK_LEAF 0xffffffffU
|
||||
#define WITNESS_RANK_BIN WITNESS_RANK_LEAF
|
||||
|
@ -8,7 +8,10 @@ extern "C" {
|
||||
#define JEMALLOC_HAVE_ATTR
|
||||
|
||||
/* Defined if alloc_size attribute is supported. */
|
||||
/* #undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE */
|
||||
#define JEMALLOC_HAVE_ATTR_ALLOC_SIZE
|
||||
|
||||
/* Defined if format_arg(...) attribute is supported. */
|
||||
#define JEMALLOC_HAVE_ATTR_FORMAT_ARG
|
||||
|
||||
/* Defined if format(gnu_printf, ...) attribute is supported. */
|
||||
/* #undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF */
|
||||
@ -69,6 +72,7 @@ extern "C" {
|
||||
# define je_malloc_stats_print malloc_stats_print
|
||||
# define je_malloc_usable_size malloc_usable_size
|
||||
# define je_mallocx mallocx
|
||||
# define je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# define je_nallocx nallocx
|
||||
# define je_posix_memalign posix_memalign
|
||||
# define je_rallocx rallocx
|
||||
@ -87,12 +91,13 @@ extern "C" {
|
||||
#include <limits.h>
|
||||
#include <strings.h>
|
||||
|
||||
#define JEMALLOC_VERSION "5.1.0-0-g61efbda7098de6fe64c362d309824864308c36d4"
|
||||
#define JEMALLOC_VERSION "5.2.1-0-gea6b3e973b477b8061e0076bb257dbd7f3faa756"
|
||||
#define JEMALLOC_VERSION_MAJOR 5
|
||||
#define JEMALLOC_VERSION_MINOR 1
|
||||
#define JEMALLOC_VERSION_BUGFIX 0
|
||||
#define JEMALLOC_VERSION_MINOR 2
|
||||
#define JEMALLOC_VERSION_BUGFIX 1
|
||||
#define JEMALLOC_VERSION_NREV 0
|
||||
#define JEMALLOC_VERSION_GID "61efbda7098de6fe64c362d309824864308c36d4"
|
||||
#define JEMALLOC_VERSION_GID "ea6b3e973b477b8061e0076bb257dbd7f3faa756"
|
||||
#define JEMALLOC_VERSION_GID_IDENT ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
|
||||
#define MALLOCX_LG_ALIGN(la) ((int)(la))
|
||||
#if LG_SIZEOF_PTR == 2
|
||||
@ -151,6 +156,7 @@ extern "C" {
|
||||
# define JEMALLOC_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
# define JEMALLOC_FORMAT_ARG(i)
|
||||
# define JEMALLOC_FORMAT_PRINTF(s, i)
|
||||
# define JEMALLOC_NOINLINE __declspec(noinline)
|
||||
# ifdef __cplusplus
|
||||
@ -178,6 +184,11 @@ extern "C" {
|
||||
# ifndef JEMALLOC_EXPORT
|
||||
# define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
|
||||
# endif
|
||||
# ifdef JEMALLOC_HAVE_ATTR_FORMAT_ARG
|
||||
# define JEMALLOC_FORMAT_ARG(i) JEMALLOC_ATTR(__format_arg__(3))
|
||||
# else
|
||||
# define JEMALLOC_FORMAT_ARG(i)
|
||||
# endif
|
||||
# ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
|
||||
# define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))
|
||||
# elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)
|
||||
@ -373,6 +384,7 @@ struct extent_hooks_s {
|
||||
# define malloc_stats_print je_malloc_stats_print
|
||||
# define malloc_usable_size je_malloc_usable_size
|
||||
# define mallocx je_mallocx
|
||||
# define smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# define nallocx je_nallocx
|
||||
# define posix_memalign je_posix_memalign
|
||||
# define rallocx je_rallocx
|
||||
@ -404,6 +416,7 @@ struct extent_hooks_s {
|
||||
# undef je_malloc_stats_print
|
||||
# undef je_malloc_usable_size
|
||||
# undef je_mallocx
|
||||
# undef je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756
|
||||
# undef je_nallocx
|
||||
# undef je_posix_memalign
|
||||
# undef je_rallocx
|
||||
|
@ -8,9 +8,11 @@
|
||||
#include "jemalloc/internal/extent_mmap.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/rtree.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/safety_check.h"
|
||||
#include "jemalloc/internal/util.h"
|
||||
|
||||
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
|
||||
@ -40,7 +42,11 @@ const uint64_t h_steps[SMOOTHSTEP_NSTEPS] = {
|
||||
#undef STEP
|
||||
};
|
||||
|
||||
static div_info_t arena_binind_div_info[NBINS];
|
||||
static div_info_t arena_binind_div_info[SC_NBINS];
|
||||
|
||||
size_t opt_oversize_threshold = OVERSIZE_THRESHOLD_DEFAULT;
|
||||
size_t oversize_threshold = OVERSIZE_THRESHOLD_DEFAULT;
|
||||
static unsigned huge_arena_ind;
|
||||
|
||||
/******************************************************************************/
|
||||
/*
|
||||
@ -61,7 +67,7 @@ static void arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
|
||||
/******************************************************************************/
|
||||
|
||||
void
|
||||
arena_basic_stats_merge(UNUSED tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
|
||||
size_t *nactive, size_t *ndirty, size_t *nmuzzy) {
|
||||
*nthreads += arena_nthreads_get(arena, false);
|
||||
@ -77,7 +83,8 @@ void
|
||||
arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
|
||||
size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,
|
||||
bin_stats_t *bstats, arena_stats_large_t *lstats) {
|
||||
bin_stats_t *bstats, arena_stats_large_t *lstats,
|
||||
arena_stats_extents_t *estats) {
|
||||
cassert(config_stats);
|
||||
|
||||
arena_basic_stats_merge(tsdn, arena, nthreads, dss, dirty_decay_ms,
|
||||
@ -94,6 +101,10 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
arena_stats_accum_zu(&astats->retained,
|
||||
extents_npages_get(&arena->extents_retained) << LG_PAGE);
|
||||
|
||||
atomic_store_zu(&astats->extent_avail,
|
||||
atomic_load_zu(&arena->extent_avail_cnt, ATOMIC_RELAXED),
|
||||
ATOMIC_RELAXED);
|
||||
|
||||
arena_stats_accum_u64(&astats->decay_dirty.npurge,
|
||||
arena_stats_read_u64(tsdn, &arena->stats,
|
||||
&arena->stats.decay_dirty.npurge));
|
||||
@ -121,8 +132,10 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
(((atomic_load_zu(&arena->nactive, ATOMIC_RELAXED) +
|
||||
extents_npages_get(&arena->extents_dirty) +
|
||||
extents_npages_get(&arena->extents_muzzy)) << LG_PAGE)));
|
||||
arena_stats_accum_zu(&astats->abandoned_vm, atomic_load_zu(
|
||||
&arena->stats.abandoned_vm, ATOMIC_RELAXED));
|
||||
|
||||
for (szind_t i = 0; i < NSIZES - NBINS; i++) {
|
||||
for (szind_t i = 0; i < SC_NSIZES - SC_NBINS; i++) {
|
||||
uint64_t nmalloc = arena_stats_read_u64(tsdn, &arena->stats,
|
||||
&arena->stats.lstats[i].nmalloc);
|
||||
arena_stats_accum_u64(&lstats[i].nmalloc, nmalloc);
|
||||
@ -140,12 +153,43 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
arena_stats_accum_u64(&astats->nrequests_large,
|
||||
nmalloc + nrequests);
|
||||
|
||||
/* nfill == nmalloc for large currently. */
|
||||
arena_stats_accum_u64(&lstats[i].nfills, nmalloc);
|
||||
arena_stats_accum_u64(&astats->nfills_large, nmalloc);
|
||||
|
||||
uint64_t nflush = arena_stats_read_u64(tsdn, &arena->stats,
|
||||
&arena->stats.lstats[i].nflushes);
|
||||
arena_stats_accum_u64(&lstats[i].nflushes, nflush);
|
||||
arena_stats_accum_u64(&astats->nflushes_large, nflush);
|
||||
|
||||
assert(nmalloc >= ndalloc);
|
||||
assert(nmalloc - ndalloc <= SIZE_T_MAX);
|
||||
size_t curlextents = (size_t)(nmalloc - ndalloc);
|
||||
lstats[i].curlextents += curlextents;
|
||||
arena_stats_accum_zu(&astats->allocated_large,
|
||||
curlextents * sz_index2size(NBINS + i));
|
||||
curlextents * sz_index2size(SC_NBINS + i));
|
||||
}
|
||||
|
||||
for (pszind_t i = 0; i < SC_NPSIZES; i++) {
|
||||
size_t dirty, muzzy, retained, dirty_bytes, muzzy_bytes,
|
||||
retained_bytes;
|
||||
dirty = extents_nextents_get(&arena->extents_dirty, i);
|
||||
muzzy = extents_nextents_get(&arena->extents_muzzy, i);
|
||||
retained = extents_nextents_get(&arena->extents_retained, i);
|
||||
dirty_bytes = extents_nbytes_get(&arena->extents_dirty, i);
|
||||
muzzy_bytes = extents_nbytes_get(&arena->extents_muzzy, i);
|
||||
retained_bytes =
|
||||
extents_nbytes_get(&arena->extents_retained, i);
|
||||
|
||||
atomic_store_zu(&estats[i].ndirty, dirty, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&estats[i].nmuzzy, muzzy, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&estats[i].nretained, retained, ATOMIC_RELAXED);
|
||||
atomic_store_zu(&estats[i].dirty_bytes, dirty_bytes,
|
||||
ATOMIC_RELAXED);
|
||||
atomic_store_zu(&estats[i].muzzy_bytes, muzzy_bytes,
|
||||
ATOMIC_RELAXED);
|
||||
atomic_store_zu(&estats[i].retained_bytes, retained_bytes,
|
||||
ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
arena_stats_unlock(tsdn, &arena->stats);
|
||||
@ -156,7 +200,7 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
cache_bin_array_descriptor_t *descriptor;
|
||||
ql_foreach(descriptor, &arena->cache_bin_array_descriptor_ql, link) {
|
||||
szind_t i = 0;
|
||||
for (; i < NBINS; i++) {
|
||||
for (; i < SC_NBINS; i++) {
|
||||
cache_bin_t *tbin = &descriptor->bins_small[i];
|
||||
arena_stats_accum_zu(&astats->tcache_bytes,
|
||||
tbin->ncached * sz_index2size(i));
|
||||
@ -200,8 +244,11 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
|
||||
nstime_update(&astats->uptime);
|
||||
nstime_subtract(&astats->uptime, &arena->create_time);
|
||||
|
||||
for (szind_t i = 0; i < NBINS; i++) {
|
||||
bin_stats_merge(tsdn, &bstats[i], &arena->bins[i]);
|
||||
for (szind_t i = 0; i < SC_NBINS; i++) {
|
||||
for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
|
||||
bin_stats_merge(tsdn, &bstats[i],
|
||||
&arena->bins[i].bin_shards[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,6 +283,54 @@ arena_slab_reg_alloc(extent_t *slab, const bin_info_t *bin_info) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
arena_slab_reg_alloc_batch(extent_t *slab, const bin_info_t *bin_info,
|
||||
unsigned cnt, void** ptrs) {
|
||||
arena_slab_data_t *slab_data = extent_slab_data_get(slab);
|
||||
|
||||
assert(extent_nfree_get(slab) >= cnt);
|
||||
assert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info));
|
||||
|
||||
#if (! defined JEMALLOC_INTERNAL_POPCOUNTL) || (defined BITMAP_USE_TREE)
|
||||
for (unsigned i = 0; i < cnt; i++) {
|
||||
size_t regind = bitmap_sfu(slab_data->bitmap,
|
||||
&bin_info->bitmap_info);
|
||||
*(ptrs + i) = (void *)((uintptr_t)extent_addr_get(slab) +
|
||||
(uintptr_t)(bin_info->reg_size * regind));
|
||||
}
|
||||
#else
|
||||
unsigned group = 0;
|
||||
bitmap_t g = slab_data->bitmap[group];
|
||||
unsigned i = 0;
|
||||
while (i < cnt) {
|
||||
while (g == 0) {
|
||||
g = slab_data->bitmap[++group];
|
||||
}
|
||||
size_t shift = group << LG_BITMAP_GROUP_NBITS;
|
||||
size_t pop = popcount_lu(g);
|
||||
if (pop > (cnt - i)) {
|
||||
pop = cnt - i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load from memory locations only once, outside the
|
||||
* hot loop below.
|
||||
*/
|
||||
uintptr_t base = (uintptr_t)extent_addr_get(slab);
|
||||
uintptr_t regsize = (uintptr_t)bin_info->reg_size;
|
||||
while (pop--) {
|
||||
size_t bit = cfs_lu(&g);
|
||||
size_t regind = shift + bit;
|
||||
*(ptrs + i) = (void *)(base + regsize * regind);
|
||||
|
||||
i++;
|
||||
}
|
||||
slab_data->bitmap[group] = g;
|
||||
}
|
||||
#endif
|
||||
extent_nfree_sub(slab, cnt);
|
||||
}
|
||||
|
||||
#ifndef JEMALLOC_JET
|
||||
static
|
||||
#endif
|
||||
@ -291,11 +386,11 @@ arena_large_malloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {
|
||||
|
||||
cassert(config_stats);
|
||||
|
||||
if (usize < LARGE_MINCLASS) {
|
||||
usize = LARGE_MINCLASS;
|
||||
if (usize < SC_LARGE_MINCLASS) {
|
||||
usize = SC_LARGE_MINCLASS;
|
||||
}
|
||||
index = sz_size2index(usize);
|
||||
hindex = (index >= NBINS) ? index - NBINS : 0;
|
||||
hindex = (index >= SC_NBINS) ? index - SC_NBINS : 0;
|
||||
|
||||
arena_stats_add_u64(tsdn, &arena->stats,
|
||||
&arena->stats.lstats[hindex].nmalloc, 1);
|
||||
@ -307,11 +402,11 @@ arena_large_dalloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {
|
||||
|
||||
cassert(config_stats);
|
||||
|
||||
if (usize < LARGE_MINCLASS) {
|
||||
usize = LARGE_MINCLASS;
|
||||
if (usize < SC_LARGE_MINCLASS) {
|
||||
usize = SC_LARGE_MINCLASS;
|
||||
}
|
||||
index = sz_size2index(usize);
|
||||
hindex = (index >= NBINS) ? index - NBINS : 0;
|
||||
hindex = (index >= SC_NBINS) ? index - SC_NBINS : 0;
|
||||
|
||||
arena_stats_add_u64(tsdn, &arena->stats,
|
||||
&arena->stats.lstats[hindex].ndalloc, 1);
|
||||
@ -324,6 +419,11 @@ arena_large_ralloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t oldusize,
|
||||
arena_large_malloc_stats_update(tsdn, arena, usize);
|
||||
}
|
||||
|
||||
static bool
|
||||
arena_may_have_muzzy(arena_t *arena) {
|
||||
return (pages_can_purge_lazy && (arena_muzzy_decay_ms_get(arena) != 0));
|
||||
}
|
||||
|
||||
extent_t *
|
||||
arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
size_t alignment, bool *zero) {
|
||||
@ -338,7 +438,7 @@ arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
extent_t *extent = extents_alloc(tsdn, arena, &extent_hooks,
|
||||
&arena->extents_dirty, NULL, usize, sz_large_pad, alignment, false,
|
||||
szind, zero, &commit);
|
||||
if (extent == NULL) {
|
||||
if (extent == NULL && arena_may_have_muzzy(arena)) {
|
||||
extent = extents_alloc(tsdn, arena, &extent_hooks,
|
||||
&arena->extents_muzzy, NULL, usize, sz_large_pad, alignment,
|
||||
false, szind, zero, &commit);
|
||||
@ -743,7 +843,7 @@ static size_t
|
||||
arena_decay_stashed(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, arena_decay_t *decay, extents_t *extents,
|
||||
bool all, extent_list_t *decay_extents, bool is_background_thread) {
|
||||
UNUSED size_t nmadvise, nunmapped;
|
||||
size_t nmadvise, nunmapped;
|
||||
size_t npurged;
|
||||
|
||||
if (config_stats) {
|
||||
@ -834,7 +934,7 @@ arena_decay_to_limit(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
|
||||
size_t npurge = arena_stash_decayed(tsdn, arena, &extent_hooks, extents,
|
||||
npages_limit, npages_decay_max, &decay_extents);
|
||||
if (npurge != 0) {
|
||||
UNUSED size_t npurged = arena_decay_stashed(tsdn, arena,
|
||||
size_t npurged = arena_decay_stashed(tsdn, arena,
|
||||
&extent_hooks, decay, extents, all, &decay_extents,
|
||||
is_background_thread);
|
||||
assert(npurged == npurge);
|
||||
@ -863,7 +963,7 @@ arena_decay_impl(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
|
||||
|
||||
bool epoch_advanced = arena_maybe_decay(tsdn, arena, decay, extents,
|
||||
is_background_thread);
|
||||
UNUSED size_t npages_new;
|
||||
size_t npages_new;
|
||||
if (epoch_advanced) {
|
||||
/* Backlog is updated on epoch advance. */
|
||||
npages_new = decay->backlog[SMOOTHSTEP_NSTEPS-1];
|
||||
@ -913,11 +1013,17 @@ static void
|
||||
arena_bin_slabs_nonfull_insert(bin_t *bin, extent_t *slab) {
|
||||
assert(extent_nfree_get(slab) > 0);
|
||||
extent_heap_insert(&bin->slabs_nonfull, slab);
|
||||
if (config_stats) {
|
||||
bin->stats.nonfull_slabs++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
arena_bin_slabs_nonfull_remove(bin_t *bin, extent_t *slab) {
|
||||
extent_heap_remove(&bin->slabs_nonfull, slab);
|
||||
if (config_stats) {
|
||||
bin->stats.nonfull_slabs--;
|
||||
}
|
||||
}
|
||||
|
||||
static extent_t *
|
||||
@ -928,6 +1034,7 @@ arena_bin_slabs_nonfull_tryget(bin_t *bin) {
|
||||
}
|
||||
if (config_stats) {
|
||||
bin->stats.reslabs++;
|
||||
bin->stats.nonfull_slabs--;
|
||||
}
|
||||
return slab;
|
||||
}
|
||||
@ -954,6 +1061,37 @@ arena_bin_slabs_full_remove(arena_t *arena, bin_t *bin, extent_t *slab) {
|
||||
extent_list_remove(&bin->slabs_full, slab);
|
||||
}
|
||||
|
||||
static void
|
||||
arena_bin_reset(tsd_t *tsd, arena_t *arena, bin_t *bin) {
|
||||
extent_t *slab;
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
if (bin->slabcur != NULL) {
|
||||
slab = bin->slabcur;
|
||||
bin->slabcur = NULL;
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
while ((slab = extent_heap_remove_first(&bin->slabs_nonfull)) != NULL) {
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
for (slab = extent_list_first(&bin->slabs_full); slab != NULL;
|
||||
slab = extent_list_first(&bin->slabs_full)) {
|
||||
arena_bin_slabs_full_remove(arena, bin, slab);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
if (config_stats) {
|
||||
bin->stats.curregs = 0;
|
||||
bin->stats.curslabs = 0;
|
||||
}
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
|
||||
void
|
||||
arena_reset(tsd_t *tsd, arena_t *arena) {
|
||||
/*
|
||||
@ -983,7 +1121,7 @@ arena_reset(tsd_t *tsd, arena_t *arena) {
|
||||
rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
|
||||
rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);
|
||||
assert(alloc_ctx.szind != NSIZES);
|
||||
assert(alloc_ctx.szind != SC_NSIZES);
|
||||
|
||||
if (config_stats || (config_prof && opt_prof)) {
|
||||
usize = sz_index2size(alloc_ctx.szind);
|
||||
@ -999,35 +1137,11 @@ arena_reset(tsd_t *tsd, arena_t *arena) {
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx);
|
||||
|
||||
/* Bins. */
|
||||
for (unsigned i = 0; i < NBINS; i++) {
|
||||
extent_t *slab;
|
||||
bin_t *bin = &arena->bins[i];
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
if (bin->slabcur != NULL) {
|
||||
slab = bin->slabcur;
|
||||
bin->slabcur = NULL;
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
for (unsigned i = 0; i < SC_NBINS; i++) {
|
||||
for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
|
||||
arena_bin_reset(tsd, arena,
|
||||
&arena->bins[i].bin_shards[j]);
|
||||
}
|
||||
while ((slab = extent_heap_remove_first(&bin->slabs_nonfull)) !=
|
||||
NULL) {
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
for (slab = extent_list_first(&bin->slabs_full); slab != NULL;
|
||||
slab = extent_list_first(&bin->slabs_full)) {
|
||||
arena_bin_slabs_full_remove(arena, bin, slab);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
if (config_stats) {
|
||||
bin->stats.curregs = 0;
|
||||
bin->stats.curslabs = 0;
|
||||
}
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
|
||||
}
|
||||
|
||||
atomic_store_zu(&arena->nactive, 0, ATOMIC_RELAXED);
|
||||
@ -1112,7 +1226,7 @@ arena_slab_alloc_hard(tsdn_t *tsdn, arena_t *arena,
|
||||
}
|
||||
|
||||
static extent_t *
|
||||
arena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
arena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind, unsigned binshard,
|
||||
const bin_info_t *bin_info) {
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
@ -1124,7 +1238,7 @@ arena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
extent_t *slab = extents_alloc(tsdn, arena, &extent_hooks,
|
||||
&arena->extents_dirty, NULL, bin_info->slab_size, 0, PAGE, true,
|
||||
binind, &zero, &commit);
|
||||
if (slab == NULL) {
|
||||
if (slab == NULL && arena_may_have_muzzy(arena)) {
|
||||
slab = extents_alloc(tsdn, arena, &extent_hooks,
|
||||
&arena->extents_muzzy, NULL, bin_info->slab_size, 0, PAGE,
|
||||
true, binind, &zero, &commit);
|
||||
@ -1140,7 +1254,7 @@ arena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
|
||||
/* Initialize slab internals. */
|
||||
arena_slab_data_t *slab_data = extent_slab_data_get(slab);
|
||||
extent_nfree_set(slab, bin_info->nregs);
|
||||
extent_nfree_binshard_set(slab, bin_info->nregs, binshard);
|
||||
bitmap_init(slab_data->bitmap, &bin_info->bitmap_info, false);
|
||||
|
||||
arena_nactive_add(arena, extent_size_get(slab) >> LG_PAGE);
|
||||
@ -1150,7 +1264,7 @@ arena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
|
||||
static extent_t *
|
||||
arena_bin_nonfull_slab_get(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
szind_t binind) {
|
||||
szind_t binind, unsigned binshard) {
|
||||
extent_t *slab;
|
||||
const bin_info_t *bin_info;
|
||||
|
||||
@ -1166,7 +1280,7 @@ arena_bin_nonfull_slab_get(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
/* Allocate a new slab. */
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
/******************************/
|
||||
slab = arena_slab_alloc(tsdn, arena, binind, bin_info);
|
||||
slab = arena_slab_alloc(tsdn, arena, binind, binshard, bin_info);
|
||||
/********************************/
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
if (slab != NULL) {
|
||||
@ -1193,7 +1307,7 @@ arena_bin_nonfull_slab_get(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
/* Re-fill bin->slabcur, then call arena_slab_reg_alloc(). */
|
||||
static void *
|
||||
arena_bin_malloc_hard(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
szind_t binind) {
|
||||
szind_t binind, unsigned binshard) {
|
||||
const bin_info_t *bin_info;
|
||||
extent_t *slab;
|
||||
|
||||
@ -1202,7 +1316,7 @@ arena_bin_malloc_hard(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
arena_bin_slabs_full_insert(arena, bin, bin->slabcur);
|
||||
bin->slabcur = NULL;
|
||||
}
|
||||
slab = arena_bin_nonfull_slab_get(tsdn, arena, bin, binind);
|
||||
slab = arena_bin_nonfull_slab_get(tsdn, arena, bin, binind, binshard);
|
||||
if (bin->slabcur != NULL) {
|
||||
/*
|
||||
* Another thread updated slabcur while this one ran without the
|
||||
@ -1246,46 +1360,75 @@ arena_bin_malloc_hard(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
return arena_slab_reg_alloc(slab, bin_info);
|
||||
}
|
||||
|
||||
/* Choose a bin shard and return the locked bin. */
|
||||
bin_t *
|
||||
arena_bin_choose_lock(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
unsigned *binshard) {
|
||||
bin_t *bin;
|
||||
if (tsdn_null(tsdn) || tsd_arena_get(tsdn_tsd(tsdn)) == NULL) {
|
||||
*binshard = 0;
|
||||
} else {
|
||||
*binshard = tsd_binshardsp_get(tsdn_tsd(tsdn))->binshard[binind];
|
||||
}
|
||||
assert(*binshard < bin_infos[binind].n_shards);
|
||||
bin = &arena->bins[binind].bin_shards[*binshard];
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
void
|
||||
arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
|
||||
cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes) {
|
||||
unsigned i, nfill;
|
||||
bin_t *bin;
|
||||
unsigned i, nfill, cnt;
|
||||
|
||||
assert(tbin->ncached == 0);
|
||||
|
||||
if (config_prof && arena_prof_accum(tsdn, arena, prof_accumbytes)) {
|
||||
prof_idump(tsdn);
|
||||
}
|
||||
bin = &arena->bins[binind];
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
|
||||
unsigned binshard;
|
||||
bin_t *bin = arena_bin_choose_lock(tsdn, arena, binind, &binshard);
|
||||
|
||||
for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>
|
||||
tcache->lg_fill_div[binind]); i < nfill; i++) {
|
||||
tcache->lg_fill_div[binind]); i < nfill; i += cnt) {
|
||||
extent_t *slab;
|
||||
void *ptr;
|
||||
if ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) >
|
||||
0) {
|
||||
ptr = arena_slab_reg_alloc(slab, &bin_infos[binind]);
|
||||
unsigned tofill = nfill - i;
|
||||
cnt = tofill < extent_nfree_get(slab) ?
|
||||
tofill : extent_nfree_get(slab);
|
||||
arena_slab_reg_alloc_batch(
|
||||
slab, &bin_infos[binind], cnt,
|
||||
tbin->avail - nfill + i);
|
||||
} else {
|
||||
ptr = arena_bin_malloc_hard(tsdn, arena, bin, binind);
|
||||
}
|
||||
if (ptr == NULL) {
|
||||
cnt = 1;
|
||||
void *ptr = arena_bin_malloc_hard(tsdn, arena, bin,
|
||||
binind, binshard);
|
||||
/*
|
||||
* OOM. tbin->avail isn't yet filled down to its first
|
||||
* element, so the successful allocations (if any) must
|
||||
* be moved just before tbin->avail before bailing out.
|
||||
*/
|
||||
if (i > 0) {
|
||||
memmove(tbin->avail - i, tbin->avail - nfill,
|
||||
i * sizeof(void *));
|
||||
if (ptr == NULL) {
|
||||
if (i > 0) {
|
||||
memmove(tbin->avail - i,
|
||||
tbin->avail - nfill,
|
||||
i * sizeof(void *));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* Insert such that low regions get used first. */
|
||||
*(tbin->avail - nfill + i) = ptr;
|
||||
}
|
||||
if (config_fill && unlikely(opt_junk_alloc)) {
|
||||
arena_alloc_junk_small(ptr, &bin_infos[binind], true);
|
||||
for (unsigned j = 0; j < cnt; j++) {
|
||||
void* ptr = *(tbin->avail - nfill + i + j);
|
||||
arena_alloc_junk_small(ptr, &bin_infos[binind],
|
||||
true);
|
||||
}
|
||||
}
|
||||
/* Insert such that low regions get used first. */
|
||||
*(tbin->avail - nfill + i) = ptr;
|
||||
}
|
||||
if (config_stats) {
|
||||
bin->stats.nmalloc += i;
|
||||
@ -1320,15 +1463,15 @@ arena_malloc_small(tsdn_t *tsdn, arena_t *arena, szind_t binind, bool zero) {
|
||||
size_t usize;
|
||||
extent_t *slab;
|
||||
|
||||
assert(binind < NBINS);
|
||||
bin = &arena->bins[binind];
|
||||
assert(binind < SC_NBINS);
|
||||
usize = sz_index2size(binind);
|
||||
unsigned binshard;
|
||||
bin = arena_bin_choose_lock(tsdn, arena, binind, &binshard);
|
||||
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
if ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) > 0) {
|
||||
ret = arena_slab_reg_alloc(slab, &bin_infos[binind]);
|
||||
} else {
|
||||
ret = arena_bin_malloc_hard(tsdn, arena, bin, binind);
|
||||
ret = arena_bin_malloc_hard(tsdn, arena, bin, binind, binshard);
|
||||
}
|
||||
|
||||
if (ret == NULL) {
|
||||
@ -1373,13 +1516,13 @@ arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind,
|
||||
assert(!tsdn_null(tsdn) || arena != NULL);
|
||||
|
||||
if (likely(!tsdn_null(tsdn))) {
|
||||
arena = arena_choose(tsdn_tsd(tsdn), arena);
|
||||
arena = arena_choose_maybe_huge(tsdn_tsd(tsdn), arena, size);
|
||||
}
|
||||
if (unlikely(arena == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (likely(size <= SMALL_MAXCLASS)) {
|
||||
if (likely(size <= SC_SMALL_MAXCLASS)) {
|
||||
return arena_malloc_small(tsdn, arena, ind, zero);
|
||||
}
|
||||
return large_malloc(tsdn, arena, sz_index2size(ind), zero);
|
||||
@ -1390,8 +1533,9 @@ arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||
bool zero, tcache_t *tcache) {
|
||||
void *ret;
|
||||
|
||||
if (usize <= SMALL_MAXCLASS && (alignment < PAGE || (alignment == PAGE
|
||||
&& (usize & PAGE_MASK) == 0))) {
|
||||
if (usize <= SC_SMALL_MAXCLASS
|
||||
&& (alignment < PAGE
|
||||
|| (alignment == PAGE && (usize & PAGE_MASK) == 0))) {
|
||||
/* Small; alignment doesn't require special slab placement. */
|
||||
ret = arena_malloc(tsdn, arena, usize, sz_size2index(usize),
|
||||
zero, tcache, true);
|
||||
@ -1406,11 +1550,15 @@ arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||
}
|
||||
|
||||
void
|
||||
arena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize) {
|
||||
arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
assert(isalloc(tsdn, ptr) == LARGE_MINCLASS);
|
||||
assert(usize <= SMALL_MAXCLASS);
|
||||
assert(isalloc(tsdn, ptr) == SC_LARGE_MINCLASS);
|
||||
assert(usize <= SC_SMALL_MAXCLASS);
|
||||
|
||||
if (config_opt_safety_checks) {
|
||||
safety_check_set_redzone(ptr, usize, SC_LARGE_MINCLASS);
|
||||
}
|
||||
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
@ -1434,15 +1582,15 @@ arena_prof_demote(tsdn_t *tsdn, extent_t *extent, const void *ptr) {
|
||||
cassert(config_prof);
|
||||
assert(ptr != NULL);
|
||||
|
||||
extent_szind_set(extent, NBINS);
|
||||
extent_szind_set(extent, SC_NBINS);
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
rtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,
|
||||
NBINS, false);
|
||||
SC_NBINS, false);
|
||||
|
||||
assert(isalloc(tsdn, ptr) == LARGE_MINCLASS);
|
||||
assert(isalloc(tsdn, ptr) == SC_LARGE_MINCLASS);
|
||||
|
||||
return LARGE_MINCLASS;
|
||||
return SC_LARGE_MINCLASS;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1452,10 +1600,19 @@ arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
assert(opt_prof);
|
||||
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
size_t usize = arena_prof_demote(tsdn, extent, ptr);
|
||||
if (usize <= tcache_maxclass) {
|
||||
size_t usize = extent_usize_get(extent);
|
||||
size_t bumped_usize = arena_prof_demote(tsdn, extent, ptr);
|
||||
if (config_opt_safety_checks && usize < SC_LARGE_MINCLASS) {
|
||||
/*
|
||||
* Currently, we only do redzoning for small sampled
|
||||
* allocations.
|
||||
*/
|
||||
assert(bumped_usize == SC_LARGE_MINCLASS);
|
||||
safety_check_verify_redzone(ptr, usize, bumped_usize);
|
||||
}
|
||||
if (bumped_usize <= tcache_maxclass && tcache != NULL) {
|
||||
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,
|
||||
sz_size2index(usize), slow_path);
|
||||
sz_size2index(bumped_usize), slow_path);
|
||||
} else {
|
||||
large_dalloc(tsdn, extent);
|
||||
}
|
||||
@ -1499,7 +1656,7 @@ arena_dalloc_bin_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
|
||||
}
|
||||
|
||||
static void
|
||||
arena_bin_lower_slab(UNUSED tsdn_t *tsdn, arena_t *arena, extent_t *slab,
|
||||
arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
|
||||
bin_t *bin) {
|
||||
assert(extent_nfree_get(slab) > 0);
|
||||
|
||||
@ -1526,11 +1683,9 @@ arena_bin_lower_slab(UNUSED tsdn_t *tsdn, arena_t *arena, extent_t *slab,
|
||||
}
|
||||
|
||||
static void
|
||||
arena_dalloc_bin_locked_impl(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
|
||||
void *ptr, bool junked) {
|
||||
arena_dalloc_bin_locked_impl(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
szind_t binind, extent_t *slab, void *ptr, bool junked) {
|
||||
arena_slab_data_t *slab_data = extent_slab_data_get(slab);
|
||||
szind_t binind = extent_szind_get(slab);
|
||||
bin_t *bin = &arena->bins[binind];
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
|
||||
if (!junked && config_fill && unlikely(opt_junk_free)) {
|
||||
@ -1554,18 +1709,21 @@ arena_dalloc_bin_locked_impl(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
|
||||
}
|
||||
|
||||
void
|
||||
arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, extent_t *extent,
|
||||
void *ptr) {
|
||||
arena_dalloc_bin_locked_impl(tsdn, arena, extent, ptr, true);
|
||||
arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
szind_t binind, extent_t *extent, void *ptr) {
|
||||
arena_dalloc_bin_locked_impl(tsdn, arena, bin, binind, extent, ptr,
|
||||
true);
|
||||
}
|
||||
|
||||
static void
|
||||
arena_dalloc_bin(tsdn_t *tsdn, arena_t *arena, extent_t *extent, void *ptr) {
|
||||
szind_t binind = extent_szind_get(extent);
|
||||
bin_t *bin = &arena->bins[binind];
|
||||
unsigned binshard = extent_binshard_get(extent);
|
||||
bin_t *bin = &arena->bins[binind].bin_shards[binshard];
|
||||
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
arena_dalloc_bin_locked_impl(tsdn, arena, extent, ptr, false);
|
||||
arena_dalloc_bin_locked_impl(tsdn, arena, bin, binind, extent, ptr,
|
||||
false);
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
|
||||
@ -1580,38 +1738,48 @@ arena_dalloc_small(tsdn_t *tsdn, void *ptr) {
|
||||
|
||||
bool
|
||||
arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, bool zero) {
|
||||
size_t extra, bool zero, size_t *newsize) {
|
||||
bool ret;
|
||||
/* Calls with non-zero extra had to clamp extra. */
|
||||
assert(extra == 0 || size + extra <= LARGE_MAXCLASS);
|
||||
|
||||
if (unlikely(size > LARGE_MAXCLASS)) {
|
||||
return true;
|
||||
}
|
||||
assert(extra == 0 || size + extra <= SC_LARGE_MAXCLASS);
|
||||
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
if (unlikely(size > SC_LARGE_MAXCLASS)) {
|
||||
ret = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
size_t usize_min = sz_s2u(size);
|
||||
size_t usize_max = sz_s2u(size + extra);
|
||||
if (likely(oldsize <= SMALL_MAXCLASS && usize_min <= SMALL_MAXCLASS)) {
|
||||
if (likely(oldsize <= SC_SMALL_MAXCLASS && usize_min
|
||||
<= SC_SMALL_MAXCLASS)) {
|
||||
/*
|
||||
* Avoid moving the allocation if the size class can be left the
|
||||
* same.
|
||||
*/
|
||||
assert(bin_infos[sz_size2index(oldsize)].reg_size ==
|
||||
oldsize);
|
||||
if ((usize_max > SMALL_MAXCLASS || sz_size2index(usize_max) !=
|
||||
sz_size2index(oldsize)) && (size > oldsize || usize_max <
|
||||
oldsize)) {
|
||||
return true;
|
||||
if ((usize_max > SC_SMALL_MAXCLASS
|
||||
|| sz_size2index(usize_max) != sz_size2index(oldsize))
|
||||
&& (size > oldsize || usize_max < oldsize)) {
|
||||
ret = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
arena_decay_tick(tsdn, extent_arena_get(extent));
|
||||
return false;
|
||||
} else if (oldsize >= LARGE_MINCLASS && usize_max >= LARGE_MINCLASS) {
|
||||
return large_ralloc_no_move(tsdn, extent, usize_min, usize_max,
|
||||
ret = false;
|
||||
} else if (oldsize >= SC_LARGE_MINCLASS
|
||||
&& usize_max >= SC_LARGE_MINCLASS) {
|
||||
ret = large_ralloc_no_move(tsdn, extent, usize_min, usize_max,
|
||||
zero);
|
||||
} else {
|
||||
ret = true;
|
||||
}
|
||||
done:
|
||||
assert(extent == iealloc(tsdn, ptr));
|
||||
*newsize = extent_usize_get(extent);
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *
|
||||
@ -1622,7 +1790,7 @@ arena_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
zero, tcache, true);
|
||||
}
|
||||
usize = sz_sa2u(usize, alignment);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) {
|
||||
return NULL;
|
||||
}
|
||||
return ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
||||
@ -1630,22 +1798,30 @@ arena_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
|
||||
void *
|
||||
arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
||||
size_t size, size_t alignment, bool zero, tcache_t *tcache) {
|
||||
size_t size, size_t alignment, bool zero, tcache_t *tcache,
|
||||
hook_ralloc_args_t *hook_args) {
|
||||
size_t usize = sz_s2u(size);
|
||||
if (unlikely(usize == 0 || size > LARGE_MAXCLASS)) {
|
||||
if (unlikely(usize == 0 || size > SC_LARGE_MAXCLASS)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (likely(usize <= SMALL_MAXCLASS)) {
|
||||
if (likely(usize <= SC_SMALL_MAXCLASS)) {
|
||||
/* Try to avoid moving the allocation. */
|
||||
if (!arena_ralloc_no_move(tsdn, ptr, oldsize, usize, 0, zero)) {
|
||||
UNUSED size_t newsize;
|
||||
if (!arena_ralloc_no_move(tsdn, ptr, oldsize, usize, 0, zero,
|
||||
&newsize)) {
|
||||
hook_invoke_expand(hook_args->is_realloc
|
||||
? hook_expand_realloc : hook_expand_rallocx,
|
||||
ptr, oldsize, usize, (uintptr_t)ptr,
|
||||
hook_args->args);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldsize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS) {
|
||||
return large_ralloc(tsdn, arena, iealloc(tsdn, ptr), usize,
|
||||
alignment, zero, tcache);
|
||||
if (oldsize >= SC_LARGE_MINCLASS
|
||||
&& usize >= SC_LARGE_MINCLASS) {
|
||||
return large_ralloc(tsdn, arena, ptr, usize,
|
||||
alignment, zero, tcache, hook_args);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1658,11 +1834,16 @@ arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hook_invoke_alloc(hook_args->is_realloc
|
||||
? hook_alloc_realloc : hook_alloc_rallocx, ret, (uintptr_t)ret,
|
||||
hook_args->args);
|
||||
hook_invoke_dalloc(hook_args->is_realloc
|
||||
? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
|
||||
|
||||
/*
|
||||
* Junk/zero-filling were already done by
|
||||
* ipalloc()/arena_malloc().
|
||||
*/
|
||||
|
||||
size_t copysize = (usize < oldsize) ? usize : oldsize;
|
||||
memcpy(ret, ptr, copysize);
|
||||
isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
|
||||
@ -1720,8 +1901,7 @@ arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena, size_t *old_limit,
|
||||
if (new_limit != NULL) {
|
||||
size_t limit = *new_limit;
|
||||
/* Grow no more than the new limit. */
|
||||
if ((new_ind = sz_psz2ind(limit + 1) - 1) >
|
||||
EXTENT_GROW_MAX_PIND) {
|
||||
if ((new_ind = sz_psz2ind(limit + 1) - 1) >= SC_NPSIZES) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1773,7 +1953,12 @@ arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
|
||||
}
|
||||
}
|
||||
|
||||
arena = (arena_t *)base_alloc(tsdn, base, sizeof(arena_t), CACHELINE);
|
||||
unsigned nbins_total = 0;
|
||||
for (i = 0; i < SC_NBINS; i++) {
|
||||
nbins_total += bin_infos[i].n_shards;
|
||||
}
|
||||
size_t arena_size = sizeof(arena_t) + sizeof(bin_t) * nbins_total;
|
||||
arena = (arena_t *)base_alloc(tsdn, base, arena_size, CACHELINE);
|
||||
if (arena == NULL) {
|
||||
goto label_error;
|
||||
}
|
||||
@ -1865,7 +2050,7 @@ arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
|
||||
}
|
||||
|
||||
arena->extent_grow_next = sz_psz2ind(HUGEPAGE);
|
||||
arena->retain_grow_limit = EXTENT_GROW_MAX_PIND;
|
||||
arena->retain_grow_limit = sz_psz2ind(SC_LARGE_MAXCLASS);
|
||||
if (malloc_mutex_init(&arena->extent_grow_mtx, "extent_grow",
|
||||
WITNESS_RANK_EXTENT_GROW, malloc_mutex_rank_exclusive)) {
|
||||
goto label_error;
|
||||
@ -1878,12 +2063,20 @@ arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
|
||||
}
|
||||
|
||||
/* Initialize bins. */
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
bool err = bin_init(&arena->bins[i]);
|
||||
if (err) {
|
||||
goto label_error;
|
||||
uintptr_t bin_addr = (uintptr_t)arena + sizeof(arena_t);
|
||||
atomic_store_u(&arena->binshard_next, 0, ATOMIC_RELEASE);
|
||||
for (i = 0; i < SC_NBINS; i++) {
|
||||
unsigned nshards = bin_infos[i].n_shards;
|
||||
arena->bins[i].bin_shards = (bin_t *)bin_addr;
|
||||
bin_addr += nshards * sizeof(bin_t);
|
||||
for (unsigned j = 0; j < nshards; j++) {
|
||||
bool err = bin_init(&arena->bins[i].bin_shards[j]);
|
||||
if (err) {
|
||||
goto label_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(bin_addr == (uintptr_t)arena + arena_size);
|
||||
|
||||
arena->base = base;
|
||||
/* Set arena before creating background threads. */
|
||||
@ -1900,8 +2093,8 @@ arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
|
||||
*/
|
||||
assert(!tsdn_null(tsdn));
|
||||
pre_reentrancy(tsdn_tsd(tsdn), arena);
|
||||
if (hooks_arena_new_hook) {
|
||||
hooks_arena_new_hook();
|
||||
if (test_hooks_arena_new_hook) {
|
||||
test_hooks_arena_new_hook();
|
||||
}
|
||||
post_reentrancy(tsdn_tsd(tsdn));
|
||||
}
|
||||
@ -1914,20 +2107,75 @@ arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
arena_t *
|
||||
arena_choose_huge(tsd_t *tsd) {
|
||||
/* huge_arena_ind can be 0 during init (will use a0). */
|
||||
if (huge_arena_ind == 0) {
|
||||
assert(!malloc_initialized());
|
||||
}
|
||||
|
||||
arena_t *huge_arena = arena_get(tsd_tsdn(tsd), huge_arena_ind, false);
|
||||
if (huge_arena == NULL) {
|
||||
/* Create the huge arena on demand. */
|
||||
assert(huge_arena_ind != 0);
|
||||
huge_arena = arena_get(tsd_tsdn(tsd), huge_arena_ind, true);
|
||||
if (huge_arena == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Purge eagerly for huge allocations, because: 1) number of
|
||||
* huge allocations is usually small, which means ticker based
|
||||
* decay is not reliable; and 2) less immediate reuse is
|
||||
* expected for huge allocations.
|
||||
*/
|
||||
if (arena_dirty_decay_ms_default_get() > 0) {
|
||||
arena_dirty_decay_ms_set(tsd_tsdn(tsd), huge_arena, 0);
|
||||
}
|
||||
if (arena_muzzy_decay_ms_default_get() > 0) {
|
||||
arena_muzzy_decay_ms_set(tsd_tsdn(tsd), huge_arena, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return huge_arena;
|
||||
}
|
||||
|
||||
bool
|
||||
arena_init_huge(void) {
|
||||
bool huge_enabled;
|
||||
|
||||
/* The threshold should be large size class. */
|
||||
if (opt_oversize_threshold > SC_LARGE_MAXCLASS ||
|
||||
opt_oversize_threshold < SC_LARGE_MINCLASS) {
|
||||
opt_oversize_threshold = 0;
|
||||
oversize_threshold = SC_LARGE_MAXCLASS + PAGE;
|
||||
huge_enabled = false;
|
||||
} else {
|
||||
/* Reserve the index for the huge arena. */
|
||||
huge_arena_ind = narenas_total_get();
|
||||
oversize_threshold = opt_oversize_threshold;
|
||||
huge_enabled = true;
|
||||
}
|
||||
|
||||
return huge_enabled;
|
||||
}
|
||||
|
||||
bool
|
||||
arena_is_huge(unsigned arena_ind) {
|
||||
if (huge_arena_ind == 0) {
|
||||
return false;
|
||||
}
|
||||
return (arena_ind == huge_arena_ind);
|
||||
}
|
||||
|
||||
void
|
||||
arena_boot(void) {
|
||||
arena_boot(sc_data_t *sc_data) {
|
||||
arena_dirty_decay_ms_default_set(opt_dirty_decay_ms);
|
||||
arena_muzzy_decay_ms_default_set(opt_muzzy_decay_ms);
|
||||
#define REGIND_bin_yes(index, reg_size) \
|
||||
div_init(&arena_binind_div_info[(index)], (reg_size));
|
||||
#define REGIND_bin_no(index, reg_size)
|
||||
#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, \
|
||||
lg_delta_lookup) \
|
||||
REGIND_bin_##bin(index, (1U<<lg_grp) + (ndelta << lg_delta))
|
||||
SIZE_CLASSES
|
||||
#undef REGIND_bin_yes
|
||||
#undef REGIND_bin_no
|
||||
#undef SC
|
||||
for (unsigned i = 0; i < SC_NBINS; i++) {
|
||||
sc_t *sc = &sc_data->sc[i];
|
||||
div_init(&arena_binind_div_info[i],
|
||||
(1U << sc->lg_base) + (sc->ndelta << sc->lg_delta));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1972,8 +2220,10 @@ arena_prefork6(tsdn_t *tsdn, arena_t *arena) {
|
||||
|
||||
void
|
||||
arena_prefork7(tsdn_t *tsdn, arena_t *arena) {
|
||||
for (unsigned i = 0; i < NBINS; i++) {
|
||||
bin_prefork(tsdn, &arena->bins[i]);
|
||||
for (unsigned i = 0; i < SC_NBINS; i++) {
|
||||
for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
|
||||
bin_prefork(tsdn, &arena->bins[i].bin_shards[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1981,8 +2231,11 @@ void
|
||||
arena_postfork_parent(tsdn_t *tsdn, arena_t *arena) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
bin_postfork_parent(tsdn, &arena->bins[i]);
|
||||
for (i = 0; i < SC_NBINS; i++) {
|
||||
for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
|
||||
bin_postfork_parent(tsdn,
|
||||
&arena->bins[i].bin_shards[j]);
|
||||
}
|
||||
}
|
||||
malloc_mutex_postfork_parent(tsdn, &arena->large_mtx);
|
||||
base_postfork_parent(tsdn, arena->base);
|
||||
@ -2025,8 +2278,10 @@ arena_postfork_child(tsdn_t *tsdn, arena_t *arena) {
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
bin_postfork_child(tsdn, &arena->bins[i]);
|
||||
for (i = 0; i < SC_NBINS; i++) {
|
||||
for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
|
||||
bin_postfork_child(tsdn, &arena->bins[i].bin_shards[j]);
|
||||
}
|
||||
}
|
||||
malloc_mutex_postfork_child(tsdn, &arena->large_mtx);
|
||||
base_postfork_child(tsdn, arena->base);
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "jemalloc/internal/assert.h"
|
||||
|
||||
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
|
||||
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
|
||||
@ -11,7 +13,7 @@
|
||||
#define BACKGROUND_THREAD_DEFAULT false
|
||||
/* Read-only after initialization. */
|
||||
bool opt_background_thread = BACKGROUND_THREAD_DEFAULT;
|
||||
size_t opt_max_background_threads = MAX_BACKGROUND_THREAD_LIMIT;
|
||||
size_t opt_max_background_threads = MAX_BACKGROUND_THREAD_LIMIT + 1;
|
||||
|
||||
/* Used for thread creation, termination and stats. */
|
||||
malloc_mutex_t background_thread_lock;
|
||||
@ -22,13 +24,9 @@ size_t max_background_threads;
|
||||
/* Thread info per-index. */
|
||||
background_thread_info_t *background_thread_info;
|
||||
|
||||
/* False if no necessary runtime support. */
|
||||
bool can_enable_background_thread;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER
|
||||
#include <dlfcn.h>
|
||||
|
||||
static int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *,
|
||||
void *(*)(void *), void *__restrict);
|
||||
@ -81,7 +79,7 @@ background_thread_info_init(tsdn_t *tsdn, background_thread_info_t *info) {
|
||||
}
|
||||
|
||||
static inline bool
|
||||
set_current_thread_affinity(UNUSED int cpu) {
|
||||
set_current_thread_affinity(int cpu) {
|
||||
#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
@ -510,6 +508,8 @@ background_thread_entry(void *ind_arg) {
|
||||
assert(thread_ind < max_background_threads);
|
||||
#ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP
|
||||
pthread_setname_np(pthread_self(), "jemalloc_bg_thd");
|
||||
#elif defined(__FreeBSD__)
|
||||
pthread_set_name_np(pthread_self(), "jemalloc_bg_thd");
|
||||
#endif
|
||||
if (opt_percpu_arena != percpu_arena_disabled) {
|
||||
set_current_thread_affinity((int)thread_ind);
|
||||
@ -534,9 +534,8 @@ background_thread_init(tsd_t *tsd, background_thread_info_t *info) {
|
||||
n_background_threads++;
|
||||
}
|
||||
|
||||
/* Create a new background thread if needed. */
|
||||
bool
|
||||
background_thread_create(tsd_t *tsd, unsigned arena_ind) {
|
||||
static bool
|
||||
background_thread_create_locked(tsd_t *tsd, unsigned arena_ind) {
|
||||
assert(have_background_thread);
|
||||
malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);
|
||||
|
||||
@ -589,6 +588,19 @@ background_thread_create(tsd_t *tsd, unsigned arena_ind) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create a new background thread if needed. */
|
||||
bool
|
||||
background_thread_create(tsd_t *tsd, unsigned arena_ind) {
|
||||
assert(have_background_thread);
|
||||
|
||||
bool ret;
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &background_thread_lock);
|
||||
ret = background_thread_create_locked(tsd, arena_ind);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &background_thread_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
background_threads_enable(tsd_t *tsd) {
|
||||
assert(n_background_threads == 0);
|
||||
@ -622,7 +634,7 @@ background_threads_enable(tsd_t *tsd) {
|
||||
}
|
||||
}
|
||||
|
||||
return background_thread_create(tsd, 0);
|
||||
return background_thread_create_locked(tsd, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -787,7 +799,13 @@ background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {
|
||||
nstime_init(&stats->run_interval, 0);
|
||||
for (unsigned i = 0; i < max_background_threads; i++) {
|
||||
background_thread_info_t *info = &background_thread_info[i];
|
||||
malloc_mutex_lock(tsdn, &info->mtx);
|
||||
if (malloc_mutex_trylock(tsdn, &info->mtx)) {
|
||||
/*
|
||||
* Each background thread run may take a long time;
|
||||
* avoid waiting on the stats if the thread is active.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (info->state != background_thread_stopped) {
|
||||
num_runs += info->tot_n_runs;
|
||||
nstime_add(&stats->run_interval, &info->tot_sleep_time);
|
||||
@ -807,21 +825,34 @@ background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {
|
||||
#undef BILLION
|
||||
#undef BACKGROUND_THREAD_MIN_INTERVAL_NS
|
||||
|
||||
#ifdef JEMALLOC_HAVE_DLSYM
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
static bool
|
||||
pthread_create_fptr_init(void) {
|
||||
if (pthread_create_fptr != NULL) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Try the next symbol first, because 1) when use lazy_lock we have a
|
||||
* wrapper for pthread_create; and 2) application may define its own
|
||||
* wrapper as well (and can call malloc within the wrapper).
|
||||
*/
|
||||
#ifdef JEMALLOC_HAVE_DLSYM
|
||||
pthread_create_fptr = dlsym(RTLD_NEXT, "pthread_create");
|
||||
#else
|
||||
pthread_create_fptr = NULL;
|
||||
#endif
|
||||
if (pthread_create_fptr == NULL) {
|
||||
can_enable_background_thread = false;
|
||||
if (config_lazy_lock || opt_background_thread) {
|
||||
if (config_lazy_lock) {
|
||||
malloc_write("<jemalloc>: Error in dlsym(RTLD_NEXT, "
|
||||
"\"pthread_create\")\n");
|
||||
abort();
|
||||
} else {
|
||||
/* Fall back to the default symbol. */
|
||||
pthread_create_fptr = pthread_create;
|
||||
}
|
||||
} else {
|
||||
can_enable_background_thread = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -866,9 +897,8 @@ background_thread_boot1(tsdn_t *tsdn) {
|
||||
assert(have_background_thread);
|
||||
assert(narenas_total_get() > 0);
|
||||
|
||||
if (opt_max_background_threads == MAX_BACKGROUND_THREAD_LIMIT &&
|
||||
ncpus < MAX_BACKGROUND_THREAD_LIMIT) {
|
||||
opt_max_background_threads = ncpus;
|
||||
if (opt_max_background_threads > MAX_BACKGROUND_THREAD_LIMIT) {
|
||||
opt_max_background_threads = DEFAULT_NUM_BACKGROUND_THREAD;
|
||||
}
|
||||
max_background_threads = opt_max_background_threads;
|
||||
|
||||
|
@ -262,8 +262,8 @@ base_block_alloc(tsdn_t *tsdn, base_t *base, extent_hooks_t *extent_hooks,
|
||||
*/
|
||||
size_t min_block_size = HUGEPAGE_CEILING(sz_psz2u(header_size + gap_size
|
||||
+ usize));
|
||||
pszind_t pind_next = (*pind_last + 1 < NPSIZES) ? *pind_last + 1 :
|
||||
*pind_last;
|
||||
pszind_t pind_next = (*pind_last + 1 < sz_psz2ind(SC_LARGE_MAXCLASS)) ?
|
||||
*pind_last + 1 : *pind_last;
|
||||
size_t next_block_size = HUGEPAGE_CEILING(sz_pind2sz(pind_next));
|
||||
size_t block_size = (min_block_size > next_block_size) ? min_block_size
|
||||
: next_block_size;
|
||||
@ -372,7 +372,7 @@ base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
|
||||
base->extent_sn_next = extent_sn_next;
|
||||
base->blocks = block;
|
||||
base->auto_thp_switched = false;
|
||||
for (szind_t i = 0; i < NSIZES; i++) {
|
||||
for (szind_t i = 0; i < SC_NSIZES; i++) {
|
||||
extent_heap_new(&base->avail[i]);
|
||||
}
|
||||
if (config_stats) {
|
||||
@ -426,7 +426,7 @@ base_alloc_impl(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment,
|
||||
|
||||
extent_t *extent = NULL;
|
||||
malloc_mutex_lock(tsdn, &base->mtx);
|
||||
for (szind_t i = sz_size2index(asize); i < NSIZES; i++) {
|
||||
for (szind_t i = sz_size2index(asize); i < SC_NSIZES; i++) {
|
||||
extent = extent_heap_remove_first(&base->avail[i]);
|
||||
if (extent != NULL) {
|
||||
/* Use existing space. */
|
||||
|
@ -1,23 +1,68 @@
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_includes.h"
|
||||
|
||||
#include "jemalloc/internal/assert.h"
|
||||
#include "jemalloc/internal/bin.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
#include "jemalloc/internal/witness.h"
|
||||
|
||||
const bin_info_t bin_infos[NBINS] = {
|
||||
#define BIN_INFO_bin_yes(reg_size, slab_size, nregs) \
|
||||
{reg_size, slab_size, nregs, BITMAP_INFO_INITIALIZER(nregs)},
|
||||
#define BIN_INFO_bin_no(reg_size, slab_size, nregs)
|
||||
#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, \
|
||||
lg_delta_lookup) \
|
||||
BIN_INFO_bin_##bin((1U<<lg_grp) + (ndelta<<lg_delta), \
|
||||
(pgs << LG_PAGE), (pgs << LG_PAGE) / ((1U<<lg_grp) + \
|
||||
(ndelta<<lg_delta)))
|
||||
SIZE_CLASSES
|
||||
#undef BIN_INFO_bin_yes
|
||||
#undef BIN_INFO_bin_no
|
||||
#undef SC
|
||||
};
|
||||
bin_info_t bin_infos[SC_NBINS];
|
||||
|
||||
static void
|
||||
bin_infos_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
|
||||
bin_info_t bin_infos[SC_NBINS]) {
|
||||
for (unsigned i = 0; i < SC_NBINS; i++) {
|
||||
bin_info_t *bin_info = &bin_infos[i];
|
||||
sc_t *sc = &sc_data->sc[i];
|
||||
bin_info->reg_size = ((size_t)1U << sc->lg_base)
|
||||
+ ((size_t)sc->ndelta << sc->lg_delta);
|
||||
bin_info->slab_size = (sc->pgs << LG_PAGE);
|
||||
bin_info->nregs =
|
||||
(uint32_t)(bin_info->slab_size / bin_info->reg_size);
|
||||
bin_info->n_shards = bin_shard_sizes[i];
|
||||
bitmap_info_t bitmap_info = BITMAP_INFO_INITIALIZER(
|
||||
bin_info->nregs);
|
||||
bin_info->bitmap_info = bitmap_info;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
bin_update_shard_size(unsigned bin_shard_sizes[SC_NBINS], size_t start_size,
|
||||
size_t end_size, size_t nshards) {
|
||||
if (nshards > BIN_SHARDS_MAX || nshards == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (start_size > SC_SMALL_MAXCLASS) {
|
||||
return false;
|
||||
}
|
||||
if (end_size > SC_SMALL_MAXCLASS) {
|
||||
end_size = SC_SMALL_MAXCLASS;
|
||||
}
|
||||
|
||||
/* Compute the index since this may happen before sz init. */
|
||||
szind_t ind1 = sz_size2index_compute(start_size);
|
||||
szind_t ind2 = sz_size2index_compute(end_size);
|
||||
for (unsigned i = ind1; i <= ind2; i++) {
|
||||
bin_shard_sizes[i] = (unsigned)nshards;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
bin_shard_sizes_boot(unsigned bin_shard_sizes[SC_NBINS]) {
|
||||
/* Load the default number of shards. */
|
||||
for (unsigned i = 0; i < SC_NBINS; i++) {
|
||||
bin_shard_sizes[i] = N_BIN_SHARDS_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bin_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
|
||||
assert(sc_data->initialized);
|
||||
bin_infos_init(sc_data, bin_shard_sizes, bin_infos);
|
||||
}
|
||||
|
||||
bool
|
||||
bin_init(bin_t *bin) {
|
||||
|
@ -275,7 +275,8 @@ ckh_grow(tsd_t *tsd, ckh_t *ckh) {
|
||||
|
||||
lg_curcells++;
|
||||
usize = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
if (unlikely(usize == 0
|
||||
|| usize > SC_LARGE_MAXCLASS)) {
|
||||
ret = true;
|
||||
goto label_return;
|
||||
}
|
||||
@ -320,7 +321,7 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh) {
|
||||
lg_prevbuckets = ckh->lg_curbuckets;
|
||||
lg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS - 1;
|
||||
usize = sz_sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) {
|
||||
return;
|
||||
}
|
||||
tab = (ckhc_t *)ipallocztm(tsd_tsdn(tsd), usize, CACHELINE, true, NULL,
|
||||
@ -396,7 +397,7 @@ ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
|
||||
ckh->keycomp = keycomp;
|
||||
|
||||
usize = sz_sa2u(sizeof(ckhc_t) << lg_mincells, CACHELINE);
|
||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||
if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) {
|
||||
ret = true;
|
||||
goto label_return;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,7 @@ mutex_pool_t extent_mutex_pool;
|
||||
size_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT;
|
||||
|
||||
static const bitmap_info_t extents_bitmap_info =
|
||||
BITMAP_INFO_INITIALIZER(NPSIZES+1);
|
||||
BITMAP_INFO_INITIALIZER(SC_NPSIZES+1);
|
||||
|
||||
static void *extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr,
|
||||
size_t size, size_t alignment, bool *zero, bool *commit,
|
||||
@ -50,20 +50,16 @@ static bool extent_purge_forced_default(extent_hooks_t *extent_hooks,
|
||||
static bool extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
|
||||
size_t length, bool growing_retained);
|
||||
#ifdef JEMALLOC_MAPS_COALESCE
|
||||
static bool extent_split_default(extent_hooks_t *extent_hooks, void *addr,
|
||||
size_t size, size_t size_a, size_t size_b, bool committed,
|
||||
unsigned arena_ind);
|
||||
#endif
|
||||
static extent_t *extent_split_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,
|
||||
szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,
|
||||
bool growing_retained);
|
||||
#ifdef JEMALLOC_MAPS_COALESCE
|
||||
static bool extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a,
|
||||
size_t size_a, void *addr_b, size_t size_b, bool committed,
|
||||
unsigned arena_ind);
|
||||
#endif
|
||||
static bool extent_merge_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b,
|
||||
bool growing_retained);
|
||||
@ -88,11 +84,9 @@ const extent_hooks_t extent_hooks_default = {
|
||||
,
|
||||
NULL
|
||||
#endif
|
||||
#ifdef JEMALLOC_MAPS_COALESCE
|
||||
,
|
||||
extent_split_default,
|
||||
extent_merge_default
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Used exclusively for gdump triggering. */
|
||||
@ -119,9 +113,13 @@ static void extent_record(tsdn_t *tsdn, arena_t *arena,
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
ph_gen(UNUSED, extent_avail_, extent_tree_t, extent_t, ph_link,
|
||||
#define ATTR_NONE /* does nothing */
|
||||
|
||||
ph_gen(ATTR_NONE, extent_avail_, extent_tree_t, extent_t, ph_link,
|
||||
extent_esnead_comp)
|
||||
|
||||
#undef ATTR_NONE
|
||||
|
||||
typedef enum {
|
||||
lock_result_success,
|
||||
lock_result_failure,
|
||||
@ -130,13 +128,16 @@ typedef enum {
|
||||
|
||||
static lock_result_t
|
||||
extent_rtree_leaf_elm_try_lock(tsdn_t *tsdn, rtree_leaf_elm_t *elm,
|
||||
extent_t **result) {
|
||||
extent_t **result, bool inactive_only) {
|
||||
extent_t *extent1 = rtree_leaf_elm_extent_read(tsdn, &extents_rtree,
|
||||
elm, true);
|
||||
|
||||
if (extent1 == NULL) {
|
||||
/* Slab implies active extents and should be skipped. */
|
||||
if (extent1 == NULL || (inactive_only && rtree_leaf_elm_slab_read(tsdn,
|
||||
&extents_rtree, elm, true))) {
|
||||
return lock_result_no_extent;
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that the extent changed out from under us, and with it
|
||||
* the leaf->extent mapping. We have to recheck while holding the lock.
|
||||
@ -159,7 +160,8 @@ extent_rtree_leaf_elm_try_lock(tsdn_t *tsdn, rtree_leaf_elm_t *elm,
|
||||
* address, and NULL otherwise.
|
||||
*/
|
||||
static extent_t *
|
||||
extent_lock_from_addr(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, void *addr) {
|
||||
extent_lock_from_addr(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, void *addr,
|
||||
bool inactive_only) {
|
||||
extent_t *ret = NULL;
|
||||
rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &extents_rtree,
|
||||
rtree_ctx, (uintptr_t)addr, false, false);
|
||||
@ -168,7 +170,8 @@ extent_lock_from_addr(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, void *addr) {
|
||||
}
|
||||
lock_result_t lock_result;
|
||||
do {
|
||||
lock_result = extent_rtree_leaf_elm_try_lock(tsdn, elm, &ret);
|
||||
lock_result = extent_rtree_leaf_elm_try_lock(tsdn, elm, &ret,
|
||||
inactive_only);
|
||||
} while (lock_result == lock_result_failure);
|
||||
return ret;
|
||||
}
|
||||
@ -182,6 +185,7 @@ extent_alloc(tsdn_t *tsdn, arena_t *arena) {
|
||||
return base_alloc_extent(tsdn, arena->base);
|
||||
}
|
||||
extent_avail_remove(&arena->extent_avail, extent);
|
||||
atomic_fetch_sub_zu(&arena->extent_avail_cnt, 1, ATOMIC_RELAXED);
|
||||
malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);
|
||||
return extent;
|
||||
}
|
||||
@ -190,6 +194,7 @@ void
|
||||
extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {
|
||||
malloc_mutex_lock(tsdn, &arena->extent_avail_mtx);
|
||||
extent_avail_insert(&arena->extent_avail, extent);
|
||||
atomic_fetch_add_zu(&arena->extent_avail_cnt, 1, ATOMIC_RELAXED);
|
||||
malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);
|
||||
}
|
||||
|
||||
@ -255,7 +260,7 @@ extent_size_quantize_ceil(size_t size) {
|
||||
size_t ret;
|
||||
|
||||
assert(size > 0);
|
||||
assert(size - sz_large_pad <= LARGE_MAXCLASS);
|
||||
assert(size - sz_large_pad <= SC_LARGE_MAXCLASS);
|
||||
assert((size & PAGE_MASK) == 0);
|
||||
|
||||
ret = extent_size_quantize_floor(size);
|
||||
@ -284,7 +289,7 @@ extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,
|
||||
malloc_mutex_rank_exclusive)) {
|
||||
return true;
|
||||
}
|
||||
for (unsigned i = 0; i < NPSIZES+1; i++) {
|
||||
for (unsigned i = 0; i < SC_NPSIZES + 1; i++) {
|
||||
extent_heap_new(&extents->heaps[i]);
|
||||
}
|
||||
bitmap_init(extents->bitmap, &extents_bitmap_info, true);
|
||||
@ -305,6 +310,32 @@ extents_npages_get(extents_t *extents) {
|
||||
return atomic_load_zu(&extents->npages, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
size_t
|
||||
extents_nextents_get(extents_t *extents, pszind_t pind) {
|
||||
return atomic_load_zu(&extents->nextents[pind], ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
size_t
|
||||
extents_nbytes_get(extents_t *extents, pszind_t pind) {
|
||||
return atomic_load_zu(&extents->nbytes[pind], ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static void
|
||||
extents_stats_add(extents_t *extent, pszind_t pind, size_t sz) {
|
||||
size_t cur = atomic_load_zu(&extent->nextents[pind], ATOMIC_RELAXED);
|
||||
atomic_store_zu(&extent->nextents[pind], cur + 1, ATOMIC_RELAXED);
|
||||
cur = atomic_load_zu(&extent->nbytes[pind], ATOMIC_RELAXED);
|
||||
atomic_store_zu(&extent->nbytes[pind], cur + sz, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static void
|
||||
extents_stats_sub(extents_t *extent, pszind_t pind, size_t sz) {
|
||||
size_t cur = atomic_load_zu(&extent->nextents[pind], ATOMIC_RELAXED);
|
||||
atomic_store_zu(&extent->nextents[pind], cur - 1, ATOMIC_RELAXED);
|
||||
cur = atomic_load_zu(&extent->nbytes[pind], ATOMIC_RELAXED);
|
||||
atomic_store_zu(&extent->nbytes[pind], cur - sz, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static void
|
||||
extents_insert_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {
|
||||
malloc_mutex_assert_owner(tsdn, &extents->mtx);
|
||||
@ -318,6 +349,11 @@ extents_insert_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {
|
||||
(size_t)pind);
|
||||
}
|
||||
extent_heap_insert(&extents->heaps[pind], extent);
|
||||
|
||||
if (config_stats) {
|
||||
extents_stats_add(extents, pind, size);
|
||||
}
|
||||
|
||||
extent_list_append(&extents->lru, extent);
|
||||
size_t npages = size >> LG_PAGE;
|
||||
/*
|
||||
@ -340,6 +376,11 @@ extents_remove_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {
|
||||
size_t psz = extent_size_quantize_floor(size);
|
||||
pszind_t pind = sz_psz2ind(psz);
|
||||
extent_heap_remove(&extents->heaps[pind], extent);
|
||||
|
||||
if (config_stats) {
|
||||
extents_stats_sub(extents, pind, size);
|
||||
}
|
||||
|
||||
if (extent_heap_empty(&extents->heaps[pind])) {
|
||||
bitmap_set(extents->bitmap, &extents_bitmap_info,
|
||||
(size_t)pind);
|
||||
@ -371,7 +412,7 @@ extents_fit_alignment(extents_t *extents, size_t min_size, size_t max_size,
|
||||
&extents_bitmap_info, (size_t)pind); i < pind_max; i =
|
||||
(pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,
|
||||
(size_t)i+1)) {
|
||||
assert(i < NPSIZES);
|
||||
assert(i < SC_NPSIZES);
|
||||
assert(!extent_heap_empty(&extents->heaps[i]));
|
||||
extent_t *extent = extent_heap_first(&extents->heaps[i]);
|
||||
uintptr_t base = (uintptr_t)extent_base_get(extent);
|
||||
@ -394,30 +435,6 @@ extents_fit_alignment(extents_t *extents, size_t min_size, size_t max_size,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Do any-best-fit extent selection, i.e. select any extent that best fits. */
|
||||
static extent_t *
|
||||
extents_best_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
|
||||
size_t size) {
|
||||
pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size));
|
||||
pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,
|
||||
(size_t)pind);
|
||||
if (i < NPSIZES+1) {
|
||||
/*
|
||||
* In order to reduce fragmentation, avoid reusing and splitting
|
||||
* large extents for much smaller sizes.
|
||||
*/
|
||||
if ((sz_pind2sz(i) >> opt_lg_extent_max_active_fit) > size) {
|
||||
return NULL;
|
||||
}
|
||||
assert(!extent_heap_empty(&extents->heaps[i]));
|
||||
extent_t *extent = extent_heap_first(&extents->heaps[i]);
|
||||
assert(extent_size_get(extent) >= size);
|
||||
return extent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do first-fit extent selection, i.e. select the oldest/lowest extent that is
|
||||
* large enough.
|
||||
@ -428,30 +445,49 @@ extents_first_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
|
||||
extent_t *ret = NULL;
|
||||
|
||||
pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size));
|
||||
|
||||
if (!maps_coalesce && !opt_retain) {
|
||||
/*
|
||||
* No split / merge allowed (Windows w/o retain). Try exact fit
|
||||
* only.
|
||||
*/
|
||||
return extent_heap_empty(&extents->heaps[pind]) ? NULL :
|
||||
extent_heap_first(&extents->heaps[pind]);
|
||||
}
|
||||
|
||||
for (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap,
|
||||
&extents_bitmap_info, (size_t)pind); i < NPSIZES+1; i =
|
||||
(pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,
|
||||
&extents_bitmap_info, (size_t)pind);
|
||||
i < SC_NPSIZES + 1;
|
||||
i = (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,
|
||||
(size_t)i+1)) {
|
||||
assert(!extent_heap_empty(&extents->heaps[i]));
|
||||
extent_t *extent = extent_heap_first(&extents->heaps[i]);
|
||||
assert(extent_size_get(extent) >= size);
|
||||
/*
|
||||
* In order to reduce fragmentation, avoid reusing and splitting
|
||||
* large extents for much smaller sizes.
|
||||
*
|
||||
* Only do check for dirty extents (delay_coalesce).
|
||||
*/
|
||||
if (extents->delay_coalesce &&
|
||||
(sz_pind2sz(i) >> opt_lg_extent_max_active_fit) > size) {
|
||||
break;
|
||||
}
|
||||
if (ret == NULL || extent_snad_comp(extent, ret) < 0) {
|
||||
ret = extent;
|
||||
}
|
||||
if (i == NPSIZES) {
|
||||
if (i == SC_NPSIZES) {
|
||||
break;
|
||||
}
|
||||
assert(i < NPSIZES);
|
||||
assert(i < SC_NPSIZES);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do {best,first}-fit extent selection, where the selection policy choice is
|
||||
* based on extents->delay_coalesce. Best-fit selection requires less
|
||||
* searching, but its layout policy is less stable and may cause higher virtual
|
||||
* memory fragmentation as a side effect.
|
||||
* Do first-fit extent selection, where the selection policy choice is
|
||||
* based on extents->delay_coalesce.
|
||||
*/
|
||||
static extent_t *
|
||||
extents_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
|
||||
@ -464,8 +500,7 @@ extents_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extent_t *extent = extents->delay_coalesce ?
|
||||
extents_best_fit_locked(tsdn, arena, extents, max_size) :
|
||||
extent_t *extent =
|
||||
extents_first_fit_locked(tsdn, arena, extents, max_size);
|
||||
|
||||
if (alignment > PAGE && extent == NULL) {
|
||||
@ -592,16 +627,24 @@ extents_evict(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
return extent;
|
||||
}
|
||||
|
||||
/*
|
||||
* This can only happen when we fail to allocate a new extent struct (which
|
||||
* indicates OOM), e.g. when trying to split an existing extent.
|
||||
*/
|
||||
static void
|
||||
extents_leak(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
extents_abandon_vm(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
extents_t *extents, extent_t *extent, bool growing_retained) {
|
||||
size_t sz = extent_size_get(extent);
|
||||
if (config_stats) {
|
||||
arena_stats_accum_zu(&arena->stats.abandoned_vm, sz);
|
||||
}
|
||||
/*
|
||||
* Leak extent after making sure its pages have already been purged, so
|
||||
* that this is only a virtual memory leak.
|
||||
*/
|
||||
if (extents_state_get(extents) == extent_state_dirty) {
|
||||
if (extent_purge_lazy_impl(tsdn, arena, r_extent_hooks,
|
||||
extent, 0, extent_size_get(extent), growing_retained)) {
|
||||
extent, 0, sz, growing_retained)) {
|
||||
extent_purge_forced_impl(tsdn, arena, r_extent_hooks,
|
||||
extent, 0, extent_size_get(extent),
|
||||
growing_retained);
|
||||
@ -748,6 +791,7 @@ extent_register_impl(tsdn_t *tsdn, extent_t *extent, bool gdump_add) {
|
||||
|
||||
if (extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, false, true,
|
||||
&elm_a, &elm_b)) {
|
||||
extent_unlock(tsdn, extent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -817,7 +861,7 @@ extent_deregister_impl(tsdn_t *tsdn, extent_t *extent, bool gdump) {
|
||||
|
||||
extent_lock(tsdn, extent);
|
||||
|
||||
extent_rtree_write_acquired(tsdn, elm_a, elm_b, NULL, NSIZES, false);
|
||||
extent_rtree_write_acquired(tsdn, elm_a, elm_b, NULL, SC_NSIZES, false);
|
||||
if (extent_slab_get(extent)) {
|
||||
extent_interior_deregister(tsdn, rtree_ctx, extent);
|
||||
extent_slab_set(extent, false);
|
||||
@ -874,7 +918,8 @@ extent_recycle_extract(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_assure_initialized(arena, r_extent_hooks);
|
||||
extent_t *extent;
|
||||
if (new_addr != NULL) {
|
||||
extent = extent_lock_from_addr(tsdn, rtree_ctx, new_addr);
|
||||
extent = extent_lock_from_addr(tsdn, rtree_ctx, new_addr,
|
||||
false);
|
||||
if (extent != NULL) {
|
||||
/*
|
||||
* We might null-out extent to report an error, but we
|
||||
@ -958,7 +1003,7 @@ extent_split_interior(tsdn_t *tsdn, arena_t *arena,
|
||||
if (leadsize != 0) {
|
||||
*lead = *extent;
|
||||
*extent = extent_split_impl(tsdn, arena, r_extent_hooks,
|
||||
*lead, leadsize, NSIZES, false, esize + trailsize, szind,
|
||||
*lead, leadsize, SC_NSIZES, false, esize + trailsize, szind,
|
||||
slab, growing_retained);
|
||||
if (*extent == NULL) {
|
||||
*to_leak = *lead;
|
||||
@ -970,7 +1015,7 @@ extent_split_interior(tsdn_t *tsdn, arena_t *arena,
|
||||
/* Split the trail. */
|
||||
if (trailsize != 0) {
|
||||
*trail = extent_split_impl(tsdn, arena, r_extent_hooks, *extent,
|
||||
esize, szind, slab, trailsize, NSIZES, false,
|
||||
esize, szind, slab, trailsize, SC_NSIZES, false,
|
||||
growing_retained);
|
||||
if (*trail == NULL) {
|
||||
*to_leak = *extent;
|
||||
@ -987,7 +1032,7 @@ extent_split_interior(tsdn_t *tsdn, arena_t *arena,
|
||||
* splitting occurred.
|
||||
*/
|
||||
extent_szind_set(*extent, szind);
|
||||
if (szind != NSIZES) {
|
||||
if (szind != SC_NSIZES) {
|
||||
rtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx,
|
||||
(uintptr_t)extent_addr_get(*extent), szind, slab);
|
||||
if (slab && extent_size_get(*extent) > PAGE) {
|
||||
@ -1023,6 +1068,17 @@ extent_recycle_split(tsdn_t *tsdn, arena_t *arena,
|
||||
&to_leak, &to_salvage, new_addr, size, pad, alignment, slab, szind,
|
||||
growing_retained);
|
||||
|
||||
if (!maps_coalesce && result != extent_split_interior_ok
|
||||
&& !opt_retain) {
|
||||
/*
|
||||
* Split isn't supported (implies Windows w/o retain). Avoid
|
||||
* leaking the extents.
|
||||
*/
|
||||
assert(to_leak != NULL && lead == NULL && trail == NULL);
|
||||
extent_deactivate(tsdn, arena, extents, to_leak);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (result == extent_split_interior_ok) {
|
||||
if (lead != NULL) {
|
||||
extent_deactivate(tsdn, arena, extents, lead);
|
||||
@ -1043,16 +1099,27 @@ extent_recycle_split(tsdn_t *tsdn, arena_t *arena,
|
||||
if (to_leak != NULL) {
|
||||
void *leak = extent_base_get(to_leak);
|
||||
extent_deregister_no_gdump_sub(tsdn, to_leak);
|
||||
extents_leak(tsdn, arena, r_extent_hooks, extents,
|
||||
extents_abandon_vm(tsdn, arena, r_extent_hooks, extents,
|
||||
to_leak, growing_retained);
|
||||
assert(extent_lock_from_addr(tsdn, rtree_ctx, leak)
|
||||
== NULL);
|
||||
assert(extent_lock_from_addr(tsdn, rtree_ctx, leak,
|
||||
false) == NULL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
static bool
|
||||
extent_need_manual_zero(arena_t *arena) {
|
||||
/*
|
||||
* Need to manually zero the extent on repopulating if either; 1) non
|
||||
* default extent hooks installed (in which case the purge semantics may
|
||||
* change); or 2) transparent huge pages enabled.
|
||||
*/
|
||||
return (!arena_has_default_hooks(arena) ||
|
||||
(opt_thp == thp_mode_always));
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to satisfy the given allocation request by reusing one of the extents
|
||||
* in the given extents_t.
|
||||
@ -1092,7 +1159,9 @@ extent_recycle(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
extent, growing_retained);
|
||||
return NULL;
|
||||
}
|
||||
extent_zeroed_set(extent, true);
|
||||
if (!extent_need_manual_zero(arena)) {
|
||||
extent_zeroed_set(extent, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (extent_committed_get(extent)) {
|
||||
@ -1115,7 +1184,8 @@ extent_recycle(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
void *addr = extent_base_get(extent);
|
||||
if (!extent_zeroed_get(extent)) {
|
||||
size_t size = extent_size_get(extent);
|
||||
if (pages_purge_forced(addr, size)) {
|
||||
if (extent_need_manual_zero(arena) ||
|
||||
pages_purge_forced(addr, size)) {
|
||||
memset(addr, 0, size);
|
||||
}
|
||||
} else if (config_debug) {
|
||||
@ -1192,7 +1262,7 @@ extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
|
||||
assert(arena != NULL);
|
||||
|
||||
return extent_alloc_default_impl(tsdn, arena, new_addr, size,
|
||||
alignment, zero, commit);
|
||||
ALIGNMENT_CEILING(alignment, PAGE), zero, commit);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1245,11 +1315,11 @@ extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
|
||||
size_t alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);
|
||||
while (alloc_size < alloc_size_min) {
|
||||
egn_skip++;
|
||||
if (arena->extent_grow_next + egn_skip == NPSIZES) {
|
||||
if (arena->extent_grow_next + egn_skip >=
|
||||
sz_psz2ind(SC_LARGE_MAXCLASS)) {
|
||||
/* Outside legal range. */
|
||||
goto label_err;
|
||||
}
|
||||
assert(arena->extent_grow_next + egn_skip < NPSIZES);
|
||||
alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);
|
||||
}
|
||||
|
||||
@ -1272,17 +1342,16 @@ extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hook_post_reentrancy(tsdn);
|
||||
}
|
||||
|
||||
extent_init(extent, arena, ptr, alloc_size, false, NSIZES,
|
||||
extent_init(extent, arena, ptr, alloc_size, false, SC_NSIZES,
|
||||
arena_extent_sn_next(arena), extent_state_active, zeroed,
|
||||
committed, true);
|
||||
committed, true, EXTENT_IS_HEAD);
|
||||
if (ptr == NULL) {
|
||||
extent_dalloc(tsdn, arena, extent);
|
||||
goto label_err;
|
||||
}
|
||||
|
||||
if (extent_register_no_gdump_add(tsdn, extent)) {
|
||||
extents_leak(tsdn, arena, r_extent_hooks,
|
||||
&arena->extents_retained, extent, true);
|
||||
extent_dalloc(tsdn, arena, extent);
|
||||
goto label_err;
|
||||
}
|
||||
|
||||
@ -1329,7 +1398,7 @@ extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
|
||||
}
|
||||
if (to_leak != NULL) {
|
||||
extent_deregister_no_gdump_sub(tsdn, to_leak);
|
||||
extents_leak(tsdn, arena, r_extent_hooks,
|
||||
extents_abandon_vm(tsdn, arena, r_extent_hooks,
|
||||
&arena->extents_retained, to_leak, true);
|
||||
}
|
||||
goto label_err;
|
||||
@ -1342,7 +1411,9 @@ extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
|
||||
&arena->extents_retained, extent, true);
|
||||
goto label_err;
|
||||
}
|
||||
extent_zeroed_set(extent, true);
|
||||
if (!extent_need_manual_zero(arena)) {
|
||||
extent_zeroed_set(extent, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1376,7 +1447,8 @@ extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
|
||||
if (*zero && !extent_zeroed_get(extent)) {
|
||||
void *addr = extent_base_get(extent);
|
||||
size_t size = extent_size_get(extent);
|
||||
if (pages_purge_forced(addr, size)) {
|
||||
if (extent_need_manual_zero(arena) ||
|
||||
pages_purge_forced(addr, size)) {
|
||||
memset(addr, 0, size);
|
||||
}
|
||||
}
|
||||
@ -1426,14 +1498,15 @@ extent_alloc_wrapper_hard(tsdn_t *tsdn, arena_t *arena,
|
||||
return NULL;
|
||||
}
|
||||
void *addr;
|
||||
size_t palignment = ALIGNMENT_CEILING(alignment, PAGE);
|
||||
if (*r_extent_hooks == &extent_hooks_default) {
|
||||
/* Call directly to propagate tsdn. */
|
||||
addr = extent_alloc_default_impl(tsdn, arena, new_addr, esize,
|
||||
alignment, zero, commit);
|
||||
palignment, zero, commit);
|
||||
} else {
|
||||
extent_hook_pre_reentrancy(tsdn, arena);
|
||||
addr = (*r_extent_hooks)->alloc(*r_extent_hooks, new_addr,
|
||||
esize, alignment, zero, commit, arena_ind_get(arena));
|
||||
esize, palignment, zero, commit, arena_ind_get(arena));
|
||||
extent_hook_post_reentrancy(tsdn);
|
||||
}
|
||||
if (addr == NULL) {
|
||||
@ -1442,13 +1515,12 @@ extent_alloc_wrapper_hard(tsdn_t *tsdn, arena_t *arena,
|
||||
}
|
||||
extent_init(extent, arena, addr, esize, slab, szind,
|
||||
arena_extent_sn_next(arena), extent_state_active, *zero, *commit,
|
||||
true);
|
||||
true, EXTENT_NOT_HEAD);
|
||||
if (pad != 0) {
|
||||
extent_addr_randomize(tsdn, extent, alignment);
|
||||
}
|
||||
if (extent_register(tsdn, extent)) {
|
||||
extents_leak(tsdn, arena, r_extent_hooks,
|
||||
&arena->extents_retained, extent, false);
|
||||
extent_dalloc(tsdn, arena, extent);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1525,9 +1597,15 @@ extent_coalesce(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
}
|
||||
|
||||
static extent_t *
|
||||
extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_try_coalesce_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
|
||||
extent_t *extent, bool *coalesced, bool growing_retained) {
|
||||
extent_t *extent, bool *coalesced, bool growing_retained,
|
||||
bool inactive_only) {
|
||||
/*
|
||||
* We avoid checking / locking inactive neighbors for large size
|
||||
* classes, since they are eagerly coalesced on deallocation which can
|
||||
* cause lock contention.
|
||||
*/
|
||||
/*
|
||||
* Continue attempting to coalesce until failure, to protect against
|
||||
* races with other threads that are thwarted by this one.
|
||||
@ -1538,7 +1616,7 @@ extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,
|
||||
|
||||
/* Try to coalesce forward. */
|
||||
extent_t *next = extent_lock_from_addr(tsdn, rtree_ctx,
|
||||
extent_past_get(extent));
|
||||
extent_past_get(extent), inactive_only);
|
||||
if (next != NULL) {
|
||||
/*
|
||||
* extents->mtx only protects against races for
|
||||
@ -1564,7 +1642,7 @@ extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,
|
||||
|
||||
/* Try to coalesce backward. */
|
||||
extent_t *prev = extent_lock_from_addr(tsdn, rtree_ctx,
|
||||
extent_before_get(extent));
|
||||
extent_before_get(extent), inactive_only);
|
||||
if (prev != NULL) {
|
||||
bool can_coalesce = extent_can_coalesce(arena, extents,
|
||||
extent, prev);
|
||||
@ -1590,6 +1668,22 @@ extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,
|
||||
return extent;
|
||||
}
|
||||
|
||||
static extent_t *
|
||||
extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
|
||||
extent_t *extent, bool *coalesced, bool growing_retained) {
|
||||
return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx,
|
||||
extents, extent, coalesced, growing_retained, false);
|
||||
}
|
||||
|
||||
static extent_t *
|
||||
extent_try_coalesce_large(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
|
||||
extent_t *extent, bool *coalesced, bool growing_retained) {
|
||||
return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx,
|
||||
extents, extent, coalesced, growing_retained, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Does the metadata management portions of putting an unused extent into the
|
||||
* given extents_t (coalesces, deregisters slab interiors, the heap operations).
|
||||
@ -1607,7 +1701,7 @@ extent_record(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
malloc_mutex_lock(tsdn, &extents->mtx);
|
||||
extent_hooks_assure_initialized(arena, r_extent_hooks);
|
||||
|
||||
extent_szind_set(extent, NSIZES);
|
||||
extent_szind_set(extent, SC_NSIZES);
|
||||
if (extent_slab_get(extent)) {
|
||||
extent_interior_deregister(tsdn, rtree_ctx, extent);
|
||||
extent_slab_set(extent, false);
|
||||
@ -1619,18 +1713,22 @@ extent_record(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
|
||||
if (!extents->delay_coalesce) {
|
||||
extent = extent_try_coalesce(tsdn, arena, r_extent_hooks,
|
||||
rtree_ctx, extents, extent, NULL, growing_retained);
|
||||
} else if (extent_size_get(extent) >= LARGE_MINCLASS) {
|
||||
} else if (extent_size_get(extent) >= SC_LARGE_MINCLASS) {
|
||||
assert(extents == &arena->extents_dirty);
|
||||
/* Always coalesce large extents eagerly. */
|
||||
bool coalesced;
|
||||
size_t prev_size;
|
||||
do {
|
||||
prev_size = extent_size_get(extent);
|
||||
assert(extent_state_get(extent) == extent_state_active);
|
||||
extent = extent_try_coalesce(tsdn, arena,
|
||||
extent = extent_try_coalesce_large(tsdn, arena,
|
||||
r_extent_hooks, rtree_ctx, extents, extent,
|
||||
&coalesced, growing_retained);
|
||||
} while (coalesced &&
|
||||
extent_size_get(extent) >= prev_size + LARGE_MINCLASS);
|
||||
} while (coalesced);
|
||||
if (extent_size_get(extent) >= oversize_threshold) {
|
||||
/* Shortcut to purge the oversize extent eagerly. */
|
||||
malloc_mutex_unlock(tsdn, &extents->mtx);
|
||||
arena_decay_extent(tsdn, arena, r_extent_hooks, extent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
extent_deactivate_locked(tsdn, arena, extents, extent);
|
||||
|
||||
@ -1645,13 +1743,18 @@ extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {
|
||||
WITNESS_RANK_CORE, 0);
|
||||
|
||||
if (extent_register(tsdn, extent)) {
|
||||
extents_leak(tsdn, arena, &extent_hooks,
|
||||
&arena->extents_retained, extent, false);
|
||||
extent_dalloc(tsdn, arena, extent);
|
||||
return;
|
||||
}
|
||||
extent_dalloc_wrapper(tsdn, arena, &extent_hooks, extent);
|
||||
}
|
||||
|
||||
static bool
|
||||
extent_may_dalloc(void) {
|
||||
/* With retain enabled, the default dalloc always fails. */
|
||||
return !opt_retain;
|
||||
}
|
||||
|
||||
static bool
|
||||
extent_dalloc_default_impl(void *addr, size_t size) {
|
||||
if (!have_dss || !extent_in_dss(addr)) {
|
||||
@ -1707,16 +1810,20 @@ extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, 0);
|
||||
|
||||
/*
|
||||
* Deregister first to avoid a race with other allocating threads, and
|
||||
* reregister if deallocation fails.
|
||||
*/
|
||||
extent_deregister(tsdn, extent);
|
||||
if (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks, extent)) {
|
||||
return;
|
||||
/* Avoid calling the default extent_dalloc unless have to. */
|
||||
if (*r_extent_hooks != &extent_hooks_default || extent_may_dalloc()) {
|
||||
/*
|
||||
* Deregister first to avoid a race with other allocating
|
||||
* threads, and reregister if deallocation fails.
|
||||
*/
|
||||
extent_deregister(tsdn, extent);
|
||||
if (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks,
|
||||
extent)) {
|
||||
return;
|
||||
}
|
||||
extent_reregister(tsdn, extent);
|
||||
}
|
||||
|
||||
extent_reregister(tsdn, extent);
|
||||
if (*r_extent_hooks != &extent_hooks_default) {
|
||||
extent_hook_pre_reentrancy(tsdn, arena);
|
||||
}
|
||||
@ -1956,13 +2063,20 @@ extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
offset, length, false);
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_MAPS_COALESCE
|
||||
static bool
|
||||
extent_split_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
|
||||
size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {
|
||||
return !maps_coalesce;
|
||||
if (!maps_coalesce) {
|
||||
/*
|
||||
* Without retain, only whole regions can be purged (required by
|
||||
* MEM_RELEASE on Windows) -- therefore disallow splitting. See
|
||||
* comments in extent_head_no_merge().
|
||||
*/
|
||||
return !opt_retain;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Accepts the extent to split, and the characteristics of each side of the
|
||||
@ -1994,7 +2108,8 @@ extent_split_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_init(trail, arena, (void *)((uintptr_t)extent_base_get(extent) +
|
||||
size_a), size_b, slab_b, szind_b, extent_sn_get(extent),
|
||||
extent_state_get(extent), extent_zeroed_get(extent),
|
||||
extent_committed_get(extent), extent_dumpable_get(extent));
|
||||
extent_committed_get(extent), extent_dumpable_get(extent),
|
||||
EXTENT_NOT_HEAD);
|
||||
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
@ -2005,7 +2120,8 @@ extent_split_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_init(&lead, arena, extent_addr_get(extent), size_a,
|
||||
slab_a, szind_a, extent_sn_get(extent),
|
||||
extent_state_get(extent), extent_zeroed_get(extent),
|
||||
extent_committed_get(extent), extent_dumpable_get(extent));
|
||||
extent_committed_get(extent), extent_dumpable_get(extent),
|
||||
EXTENT_NOT_HEAD);
|
||||
|
||||
extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, &lead, false,
|
||||
true, &lead_elm_a, &lead_elm_b);
|
||||
@ -2063,7 +2179,7 @@ extent_split_wrapper(tsdn_t *tsdn, arena_t *arena,
|
||||
|
||||
static bool
|
||||
extent_merge_default_impl(void *addr_a, void *addr_b) {
|
||||
if (!maps_coalesce) {
|
||||
if (!maps_coalesce && !opt_retain) {
|
||||
return true;
|
||||
}
|
||||
if (have_dss && !extent_dss_mergeable(addr_a, addr_b)) {
|
||||
@ -2073,13 +2189,51 @@ extent_merge_default_impl(void *addr_a, void *addr_b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_MAPS_COALESCE
|
||||
/*
|
||||
* Returns true if the given extents can't be merged because of their head bit
|
||||
* settings. Assumes the second extent has the higher address.
|
||||
*/
|
||||
static bool
|
||||
extent_head_no_merge(extent_t *a, extent_t *b) {
|
||||
assert(extent_base_get(a) < extent_base_get(b));
|
||||
/*
|
||||
* When coalesce is not always allowed (Windows), only merge extents
|
||||
* from the same VirtualAlloc region under opt.retain (in which case
|
||||
* MEM_DECOMMIT is utilized for purging).
|
||||
*/
|
||||
if (maps_coalesce) {
|
||||
return false;
|
||||
}
|
||||
if (!opt_retain) {
|
||||
return true;
|
||||
}
|
||||
/* If b is a head extent, disallow the cross-region merge. */
|
||||
if (extent_is_head_get(b)) {
|
||||
/*
|
||||
* Additionally, sn should not overflow with retain; sanity
|
||||
* check that different regions have unique sn.
|
||||
*/
|
||||
assert(extent_sn_comp(a, b) != 0);
|
||||
return true;
|
||||
}
|
||||
assert(extent_sn_comp(a, b) == 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
|
||||
void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {
|
||||
if (!maps_coalesce) {
|
||||
tsdn_t *tsdn = tsdn_fetch();
|
||||
extent_t *a = iealloc(tsdn, addr_a);
|
||||
extent_t *b = iealloc(tsdn, addr_b);
|
||||
if (extent_head_no_merge(a, b)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return extent_merge_default_impl(addr_a, addr_b);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
extent_merge_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
@ -2087,10 +2241,11 @@ extent_merge_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
bool growing_retained) {
|
||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
||||
assert(extent_base_get(a) < extent_base_get(b));
|
||||
|
||||
extent_hooks_assure_initialized(arena, r_extent_hooks);
|
||||
|
||||
if ((*r_extent_hooks)->merge == NULL) {
|
||||
if ((*r_extent_hooks)->merge == NULL || extent_head_no_merge(a, b)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2129,22 +2284,23 @@ extent_merge_impl(tsdn_t *tsdn, arena_t *arena,
|
||||
|
||||
if (a_elm_b != NULL) {
|
||||
rtree_leaf_elm_write(tsdn, &extents_rtree, a_elm_b, NULL,
|
||||
NSIZES, false);
|
||||
SC_NSIZES, false);
|
||||
}
|
||||
if (b_elm_b != NULL) {
|
||||
rtree_leaf_elm_write(tsdn, &extents_rtree, b_elm_a, NULL,
|
||||
NSIZES, false);
|
||||
SC_NSIZES, false);
|
||||
} else {
|
||||
b_elm_b = b_elm_a;
|
||||
}
|
||||
|
||||
extent_size_set(a, extent_size_get(a) + extent_size_get(b));
|
||||
extent_szind_set(a, NSIZES);
|
||||
extent_szind_set(a, SC_NSIZES);
|
||||
extent_sn_set(a, (extent_sn_get(a) < extent_sn_get(b)) ?
|
||||
extent_sn_get(a) : extent_sn_get(b));
|
||||
extent_zeroed_set(a, extent_zeroed_get(a) && extent_zeroed_get(b));
|
||||
|
||||
extent_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, NSIZES, false);
|
||||
extent_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, SC_NSIZES,
|
||||
false);
|
||||
|
||||
extent_unlock2(tsdn, a, b);
|
||||
|
||||
@ -2176,3 +2332,72 @@ extent_boot(void) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
extent_util_stats_get(tsdn_t *tsdn, const void *ptr,
|
||||
size_t *nfree, size_t *nregs, size_t *size) {
|
||||
assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL);
|
||||
|
||||
const extent_t *extent = iealloc(tsdn, ptr);
|
||||
if (unlikely(extent == NULL)) {
|
||||
*nfree = *nregs = *size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
*size = extent_size_get(extent);
|
||||
if (!extent_slab_get(extent)) {
|
||||
*nfree = 0;
|
||||
*nregs = 1;
|
||||
} else {
|
||||
*nfree = extent_nfree_get(extent);
|
||||
*nregs = bin_infos[extent_szind_get(extent)].nregs;
|
||||
assert(*nfree <= *nregs);
|
||||
assert(*nfree * extent_usize_get(extent) <= *size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
|
||||
size_t *nfree, size_t *nregs, size_t *size,
|
||||
size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr) {
|
||||
assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL
|
||||
&& bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL);
|
||||
|
||||
const extent_t *extent = iealloc(tsdn, ptr);
|
||||
if (unlikely(extent == NULL)) {
|
||||
*nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0;
|
||||
*slabcur_addr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*size = extent_size_get(extent);
|
||||
if (!extent_slab_get(extent)) {
|
||||
*nfree = *bin_nfree = *bin_nregs = 0;
|
||||
*nregs = 1;
|
||||
*slabcur_addr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*nfree = extent_nfree_get(extent);
|
||||
const szind_t szind = extent_szind_get(extent);
|
||||
*nregs = bin_infos[szind].nregs;
|
||||
assert(*nfree <= *nregs);
|
||||
assert(*nfree * extent_usize_get(extent) <= *size);
|
||||
|
||||
const arena_t *arena = extent_arena_get(extent);
|
||||
assert(arena != NULL);
|
||||
const unsigned binshard = extent_binshard_get(extent);
|
||||
bin_t *bin = &arena->bins[szind].bin_shards[binshard];
|
||||
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
if (config_stats) {
|
||||
*bin_nregs = *nregs * bin->stats.curslabs;
|
||||
assert(*bin_nregs >= bin->stats.curregs);
|
||||
*bin_nfree = *bin_nregs - bin->stats.curregs;
|
||||
} else {
|
||||
*bin_nfree = *bin_nregs = 0;
|
||||
}
|
||||
*slabcur_addr = extent_addr_get(bin->slabcur);
|
||||
assert(*slabcur_addr != NULL);
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
|
||||
|
||||
cassert(have_dss);
|
||||
assert(size > 0);
|
||||
assert(alignment > 0);
|
||||
assert(alignment == ALIGNMENT_CEILING(alignment, PAGE));
|
||||
|
||||
/*
|
||||
* sbrk() uses a signed increment argument, so take care not to
|
||||
@ -154,9 +154,10 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
|
||||
(uintptr_t)gap_addr_page;
|
||||
if (gap_size_page != 0) {
|
||||
extent_init(gap, arena, gap_addr_page,
|
||||
gap_size_page, false, NSIZES,
|
||||
gap_size_page, false, SC_NSIZES,
|
||||
arena_extent_sn_next(arena),
|
||||
extent_state_active, false, true, true);
|
||||
extent_state_active, false, true, true,
|
||||
EXTENT_NOT_HEAD);
|
||||
}
|
||||
/*
|
||||
* Compute the address just past the end of the desired
|
||||
@ -198,9 +199,9 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
|
||||
extent_t extent;
|
||||
|
||||
extent_init(&extent, arena, ret, size,
|
||||
size, false, NSIZES,
|
||||
size, false, SC_NSIZES,
|
||||
extent_state_active, false, true,
|
||||
true);
|
||||
true, EXTENT_NOT_HEAD);
|
||||
if (extent_purge_forced_wrapper(tsdn,
|
||||
arena, &extent_hooks, &extent, 0,
|
||||
size)) {
|
||||
|
@ -21,8 +21,8 @@ bool opt_retain =
|
||||
void *
|
||||
extent_alloc_mmap(void *new_addr, size_t size, size_t alignment, bool *zero,
|
||||
bool *commit) {
|
||||
void *ret = pages_map(new_addr, size, ALIGNMENT_CEILING(alignment,
|
||||
PAGE), commit);
|
||||
assert(alignment == ALIGNMENT_CEILING(alignment, PAGE));
|
||||
void *ret = pages_map(new_addr, size, alignment, commit);
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
195
contrib/jemalloc/src/hook.c
Normal file
195
contrib/jemalloc/src/hook.c
Normal file
@ -0,0 +1,195 @@
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
|
||||
#include "jemalloc/internal/hook.h"
|
||||
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/seq.h"
|
||||
|
||||
typedef struct hooks_internal_s hooks_internal_t;
|
||||
struct hooks_internal_s {
|
||||
hooks_t hooks;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
seq_define(hooks_internal_t, hooks)
|
||||
|
||||
static atomic_u_t nhooks = ATOMIC_INIT(0);
|
||||
static seq_hooks_t hooks[HOOK_MAX];
|
||||
static malloc_mutex_t hooks_mu;
|
||||
|
||||
bool
|
||||
hook_boot() {
|
||||
return malloc_mutex_init(&hooks_mu, "hooks", WITNESS_RANK_HOOK,
|
||||
malloc_mutex_rank_exclusive);
|
||||
}
|
||||
|
||||
static void *
|
||||
hook_install_locked(hooks_t *to_install) {
|
||||
hooks_internal_t hooks_internal;
|
||||
for (int i = 0; i < HOOK_MAX; i++) {
|
||||
bool success = seq_try_load_hooks(&hooks_internal, &hooks[i]);
|
||||
/* We hold mu; no concurrent access. */
|
||||
assert(success);
|
||||
if (!hooks_internal.in_use) {
|
||||
hooks_internal.hooks = *to_install;
|
||||
hooks_internal.in_use = true;
|
||||
seq_store_hooks(&hooks[i], &hooks_internal);
|
||||
atomic_store_u(&nhooks,
|
||||
atomic_load_u(&nhooks, ATOMIC_RELAXED) + 1,
|
||||
ATOMIC_RELAXED);
|
||||
return &hooks[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
hook_install(tsdn_t *tsdn, hooks_t *to_install) {
|
||||
malloc_mutex_lock(tsdn, &hooks_mu);
|
||||
void *ret = hook_install_locked(to_install);
|
||||
if (ret != NULL) {
|
||||
tsd_global_slow_inc(tsdn);
|
||||
}
|
||||
malloc_mutex_unlock(tsdn, &hooks_mu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
hook_remove_locked(seq_hooks_t *to_remove) {
|
||||
hooks_internal_t hooks_internal;
|
||||
bool success = seq_try_load_hooks(&hooks_internal, to_remove);
|
||||
/* We hold mu; no concurrent access. */
|
||||
assert(success);
|
||||
/* Should only remove hooks that were added. */
|
||||
assert(hooks_internal.in_use);
|
||||
hooks_internal.in_use = false;
|
||||
seq_store_hooks(to_remove, &hooks_internal);
|
||||
atomic_store_u(&nhooks, atomic_load_u(&nhooks, ATOMIC_RELAXED) - 1,
|
||||
ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
void
|
||||
hook_remove(tsdn_t *tsdn, void *opaque) {
|
||||
if (config_debug) {
|
||||
char *hooks_begin = (char *)&hooks[0];
|
||||
char *hooks_end = (char *)&hooks[HOOK_MAX];
|
||||
char *hook = (char *)opaque;
|
||||
assert(hooks_begin <= hook && hook < hooks_end
|
||||
&& (hook - hooks_begin) % sizeof(seq_hooks_t) == 0);
|
||||
}
|
||||
malloc_mutex_lock(tsdn, &hooks_mu);
|
||||
hook_remove_locked((seq_hooks_t *)opaque);
|
||||
tsd_global_slow_dec(tsdn);
|
||||
malloc_mutex_unlock(tsdn, &hooks_mu);
|
||||
}
|
||||
|
||||
#define FOR_EACH_HOOK_BEGIN(hooks_internal_ptr) \
|
||||
for (int for_each_hook_counter = 0; \
|
||||
for_each_hook_counter < HOOK_MAX; \
|
||||
for_each_hook_counter++) { \
|
||||
bool for_each_hook_success = seq_try_load_hooks( \
|
||||
(hooks_internal_ptr), &hooks[for_each_hook_counter]); \
|
||||
if (!for_each_hook_success) { \
|
||||
continue; \
|
||||
} \
|
||||
if (!(hooks_internal_ptr)->in_use) { \
|
||||
continue; \
|
||||
}
|
||||
#define FOR_EACH_HOOK_END \
|
||||
}
|
||||
|
||||
static bool *
|
||||
hook_reentrantp() {
|
||||
/*
|
||||
* We prevent user reentrancy within hooks. This is basically just a
|
||||
* thread-local bool that triggers an early-exit.
|
||||
*
|
||||
* We don't fold in_hook into reentrancy. There are two reasons for
|
||||
* this:
|
||||
* - Right now, we turn on reentrancy during things like extent hook
|
||||
* execution. Allocating during extent hooks is not officially
|
||||
* supported, but we don't want to break it for the time being. These
|
||||
* sorts of allocations should probably still be hooked, though.
|
||||
* - If a hook allocates, we may want it to be relatively fast (after
|
||||
* all, it executes on every allocator operation). Turning on
|
||||
* reentrancy is a fairly heavyweight mode (disabling tcache,
|
||||
* redirecting to arena 0, etc.). It's possible we may one day want
|
||||
* to turn on reentrant mode here, if it proves too difficult to keep
|
||||
* this working. But that's fairly easy for us to see; OTOH, people
|
||||
* not using hooks because they're too slow is easy for us to miss.
|
||||
*
|
||||
* The tricky part is
|
||||
* that this code might get invoked even if we don't have access to tsd.
|
||||
* This function mimics getting a pointer to thread-local data, except
|
||||
* that it might secretly return a pointer to some global data if we
|
||||
* know that the caller will take the early-exit path.
|
||||
* If we return a bool that indicates that we are reentrant, then the
|
||||
* caller will go down the early exit path, leaving the global
|
||||
* untouched.
|
||||
*/
|
||||
static bool in_hook_global = true;
|
||||
tsdn_t *tsdn = tsdn_fetch();
|
||||
tcache_t *tcache = tsdn_tcachep_get(tsdn);
|
||||
if (tcache != NULL) {
|
||||
return &tcache->in_hook;
|
||||
}
|
||||
return &in_hook_global;
|
||||
}
|
||||
|
||||
#define HOOK_PROLOGUE \
|
||||
if (likely(atomic_load_u(&nhooks, ATOMIC_RELAXED) == 0)) { \
|
||||
return; \
|
||||
} \
|
||||
bool *in_hook = hook_reentrantp(); \
|
||||
if (*in_hook) { \
|
||||
return; \
|
||||
} \
|
||||
*in_hook = true;
|
||||
|
||||
#define HOOK_EPILOGUE \
|
||||
*in_hook = false;
|
||||
|
||||
void
|
||||
hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
|
||||
uintptr_t args_raw[3]) {
|
||||
HOOK_PROLOGUE
|
||||
|
||||
hooks_internal_t hook;
|
||||
FOR_EACH_HOOK_BEGIN(&hook)
|
||||
hook_alloc h = hook.hooks.alloc_hook;
|
||||
if (h != NULL) {
|
||||
h(hook.hooks.extra, type, result, result_raw, args_raw);
|
||||
}
|
||||
FOR_EACH_HOOK_END
|
||||
|
||||
HOOK_EPILOGUE
|
||||
}
|
||||
|
||||
void
|
||||
hook_invoke_dalloc(hook_dalloc_t type, void *address, uintptr_t args_raw[3]) {
|
||||
HOOK_PROLOGUE
|
||||
hooks_internal_t hook;
|
||||
FOR_EACH_HOOK_BEGIN(&hook)
|
||||
hook_dalloc h = hook.hooks.dalloc_hook;
|
||||
if (h != NULL) {
|
||||
h(hook.hooks.extra, type, address, args_raw);
|
||||
}
|
||||
FOR_EACH_HOOK_END
|
||||
HOOK_EPILOGUE
|
||||
}
|
||||
|
||||
void
|
||||
hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
|
||||
size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]) {
|
||||
HOOK_PROLOGUE
|
||||
hooks_internal_t hook;
|
||||
FOR_EACH_HOOK_BEGIN(&hook)
|
||||
hook_expand h = hook.hooks.expand_hook;
|
||||
if (h != NULL) {
|
||||
h(hook.hooks.extra, type, address, old_usize, new_usize,
|
||||
result_raw, args_raw);
|
||||
}
|
||||
FOR_EACH_HOOK_END
|
||||
HOOK_EPILOGUE
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@ large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||
assert(!tsdn_null(tsdn) || arena != NULL);
|
||||
|
||||
ausize = sz_sa2u(usize, alignment);
|
||||
if (unlikely(ausize == 0 || ausize > LARGE_MAXCLASS)) {
|
||||
if (unlikely(ausize == 0 || ausize > SC_LARGE_MAXCLASS)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||
*/
|
||||
is_zeroed = zero;
|
||||
if (likely(!tsdn_null(tsdn))) {
|
||||
arena = arena_choose(tsdn_tsd(tsdn), arena);
|
||||
arena = arena_choose_maybe_huge(tsdn_tsd(tsdn), arena, usize);
|
||||
}
|
||||
if (unlikely(arena == NULL) || (extent = arena_extent_alloc_large(tsdn,
|
||||
arena, usize, alignment, &is_zeroed)) == NULL) {
|
||||
@ -109,7 +109,7 @@ large_ralloc_no_move_shrink(tsdn_t *tsdn, extent_t *extent, size_t usize) {
|
||||
if (diff != 0) {
|
||||
extent_t *trail = extent_split_wrapper(tsdn, arena,
|
||||
&extent_hooks, extent, usize + sz_large_pad,
|
||||
sz_size2index(usize), false, diff, NSIZES, false);
|
||||
sz_size2index(usize), false, diff, SC_NSIZES, false);
|
||||
if (trail == NULL) {
|
||||
return true;
|
||||
}
|
||||
@ -154,17 +154,17 @@ large_ralloc_no_move_expand(tsdn_t *tsdn, extent_t *extent, size_t usize,
|
||||
bool new_mapping;
|
||||
if ((trail = extents_alloc(tsdn, arena, &extent_hooks,
|
||||
&arena->extents_dirty, extent_past_get(extent), trailsize, 0,
|
||||
CACHELINE, false, NSIZES, &is_zeroed_trail, &commit)) != NULL
|
||||
CACHELINE, false, SC_NSIZES, &is_zeroed_trail, &commit)) != NULL
|
||||
|| (trail = extents_alloc(tsdn, arena, &extent_hooks,
|
||||
&arena->extents_muzzy, extent_past_get(extent), trailsize, 0,
|
||||
CACHELINE, false, NSIZES, &is_zeroed_trail, &commit)) != NULL) {
|
||||
CACHELINE, false, SC_NSIZES, &is_zeroed_trail, &commit)) != NULL) {
|
||||
if (config_stats) {
|
||||
new_mapping = false;
|
||||
}
|
||||
} else {
|
||||
if ((trail = extent_alloc_wrapper(tsdn, arena, &extent_hooks,
|
||||
extent_past_get(extent), trailsize, 0, CACHELINE, false,
|
||||
NSIZES, &is_zeroed_trail, &commit)) == NULL) {
|
||||
SC_NSIZES, &is_zeroed_trail, &commit)) == NULL) {
|
||||
return true;
|
||||
}
|
||||
if (config_stats) {
|
||||
@ -221,9 +221,10 @@ large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
|
||||
size_t oldusize = extent_usize_get(extent);
|
||||
|
||||
/* The following should have been caught by callers. */
|
||||
assert(usize_min > 0 && usize_max <= LARGE_MAXCLASS);
|
||||
assert(usize_min > 0 && usize_max <= SC_LARGE_MAXCLASS);
|
||||
/* Both allocation sizes must be large to avoid a move. */
|
||||
assert(oldusize >= LARGE_MINCLASS && usize_max >= LARGE_MINCLASS);
|
||||
assert(oldusize >= SC_LARGE_MINCLASS
|
||||
&& usize_max >= SC_LARGE_MINCLASS);
|
||||
|
||||
if (usize_max > oldusize) {
|
||||
/* Attempt to expand the allocation in-place. */
|
||||
@ -270,17 +271,23 @@ large_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
}
|
||||
|
||||
void *
|
||||
large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache) {
|
||||
size_t oldusize = extent_usize_get(extent);
|
||||
large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
|
||||
size_t alignment, bool zero, tcache_t *tcache,
|
||||
hook_ralloc_args_t *hook_args) {
|
||||
extent_t *extent = iealloc(tsdn, ptr);
|
||||
|
||||
size_t oldusize = extent_usize_get(extent);
|
||||
/* The following should have been caught by callers. */
|
||||
assert(usize > 0 && usize <= LARGE_MAXCLASS);
|
||||
assert(usize > 0 && usize <= SC_LARGE_MAXCLASS);
|
||||
/* Both allocation sizes must be large to avoid a move. */
|
||||
assert(oldusize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS);
|
||||
assert(oldusize >= SC_LARGE_MINCLASS
|
||||
&& usize >= SC_LARGE_MINCLASS);
|
||||
|
||||
/* Try to avoid moving the allocation. */
|
||||
if (!large_ralloc_no_move(tsdn, extent, usize, usize, zero)) {
|
||||
hook_invoke_expand(hook_args->is_realloc
|
||||
? hook_expand_realloc : hook_expand_rallocx, ptr, oldusize,
|
||||
usize, (uintptr_t)ptr, hook_args->args);
|
||||
return extent_addr_get(extent);
|
||||
}
|
||||
|
||||
@ -295,6 +302,12 @@ large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hook_invoke_alloc(hook_args->is_realloc
|
||||
? hook_alloc_realloc : hook_alloc_rallocx, ret, (uintptr_t)ret,
|
||||
hook_args->args);
|
||||
hook_invoke_dalloc(hook_args->is_realloc
|
||||
? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
|
||||
|
||||
size_t copysize = (usize < oldusize) ? usize : oldusize;
|
||||
memcpy(ret, extent_addr_get(extent), copysize);
|
||||
isdalloct(tsdn, extent_addr_get(extent), oldusize, tcache, NULL, true);
|
||||
@ -318,8 +331,9 @@ large_dalloc_prep_impl(tsdn_t *tsdn, arena_t *arena, extent_t *extent,
|
||||
large_dalloc_maybe_junk(extent_addr_get(extent),
|
||||
extent_usize_get(extent));
|
||||
} else {
|
||||
malloc_mutex_assert_owner(tsdn, &arena->large_mtx);
|
||||
/* Only hold the large_mtx if necessary. */
|
||||
if (!arena_is_auto(arena)) {
|
||||
malloc_mutex_assert_owner(tsdn, &arena->large_mtx);
|
||||
extent_list_remove(&arena->large, extent);
|
||||
}
|
||||
}
|
||||
@ -369,3 +383,13 @@ void
|
||||
large_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent) {
|
||||
large_prof_tctx_set(tsdn, extent, (prof_tctx_t *)(uintptr_t)1U);
|
||||
}
|
||||
|
||||
nstime_t
|
||||
large_prof_alloc_time_get(const extent_t *extent) {
|
||||
return extent_prof_alloc_time_get(extent);
|
||||
}
|
||||
|
||||
void
|
||||
large_prof_alloc_time_set(extent_t *extent, nstime_t t) {
|
||||
extent_prof_alloc_time_set(extent, t);
|
||||
}
|
||||
|
@ -646,7 +646,6 @@ malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
*/
|
||||
write_cb = (je_malloc_message != NULL) ? je_malloc_message :
|
||||
wrtmessage;
|
||||
cbopaque = NULL;
|
||||
}
|
||||
|
||||
malloc_vsnprintf(buf, sizeof(buf), format, ap);
|
||||
|
@ -57,7 +57,7 @@ _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
|
||||
void
|
||||
malloc_mutex_lock_slow(malloc_mutex_t *mutex) {
|
||||
mutex_prof_data_t *data = &mutex->prof_data;
|
||||
UNUSED nstime_t before = NSTIME_ZERO_INITIALIZER;
|
||||
nstime_t before = NSTIME_ZERO_INITIALIZER;
|
||||
|
||||
if (ncpus == 1) {
|
||||
goto label_spin_done;
|
||||
@ -66,7 +66,8 @@ malloc_mutex_lock_slow(malloc_mutex_t *mutex) {
|
||||
int cnt = 0, max_cnt = MALLOC_MUTEX_MAX_SPIN;
|
||||
do {
|
||||
spin_cpu_spinwait();
|
||||
if (!malloc_mutex_trylock_final(mutex)) {
|
||||
if (!atomic_load_b(&mutex->locked, ATOMIC_RELAXED)
|
||||
&& !malloc_mutex_trylock_final(mutex)) {
|
||||
data->n_spin_acquired++;
|
||||
return;
|
||||
}
|
||||
@ -165,9 +166,7 @@ malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
|
||||
}
|
||||
# endif
|
||||
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
|
||||
mutex->lock = OS_UNFAIR_LOCK_INIT;
|
||||
#elif (defined(JEMALLOC_OSSPIN))
|
||||
mutex->lock = 0;
|
||||
mutex->lock = OS_UNFAIR_LOCK_INIT;
|
||||
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
|
||||
if (postpone_init) {
|
||||
mutex->postponed_next = postponed_mutexes;
|
||||
|
@ -290,7 +290,7 @@ pages_decommit(void *addr, size_t size) {
|
||||
|
||||
bool
|
||||
pages_purge_lazy(void *addr, size_t size) {
|
||||
assert(PAGE_ADDR2BASE(addr) == addr);
|
||||
assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);
|
||||
assert(PAGE_CEILING(size) == size);
|
||||
|
||||
if (!pages_can_purge_lazy) {
|
||||
@ -420,6 +420,10 @@ os_page_detect(void) {
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
#elif defined(__FreeBSD__)
|
||||
/*
|
||||
* This returns the value obtained from
|
||||
* the auxv vector, avoiding a syscall.
|
||||
*/
|
||||
return getpagesize();
|
||||
#else
|
||||
long result = sysconf(_SC_PAGESIZE);
|
||||
@ -573,6 +577,10 @@ init_thp_state(void) {
|
||||
close(fd);
|
||||
#endif
|
||||
|
||||
if (nread < 0) {
|
||||
goto label_error;
|
||||
}
|
||||
|
||||
if (strncmp(buf, sys_state_madvise, (size_t)nread) == 0) {
|
||||
init_system_thp_mode = thp_mode_default;
|
||||
} else if (strncmp(buf, sys_state_always, (size_t)nread) == 0) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "jemalloc/internal/hash.h"
|
||||
#include "jemalloc/internal/malloc_io.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/emitter.h"
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@ -23,7 +24,7 @@
|
||||
*/
|
||||
#undef _Unwind_Backtrace
|
||||
#include <unwind.h>
|
||||
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, hooks_libc_hook)
|
||||
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
@ -38,6 +39,7 @@ bool opt_prof_gdump = false;
|
||||
bool opt_prof_final = false;
|
||||
bool opt_prof_leak = false;
|
||||
bool opt_prof_accum = false;
|
||||
bool opt_prof_log = false;
|
||||
char opt_prof_prefix[
|
||||
/* Minimize memory bloat for non-prof builds. */
|
||||
#ifdef JEMALLOC_PROF
|
||||
@ -70,6 +72,100 @@ uint64_t prof_interval = 0;
|
||||
|
||||
size_t lg_prof_sample;
|
||||
|
||||
typedef enum prof_logging_state_e prof_logging_state_t;
|
||||
enum prof_logging_state_e {
|
||||
prof_logging_state_stopped,
|
||||
prof_logging_state_started,
|
||||
prof_logging_state_dumping
|
||||
};
|
||||
|
||||
/*
|
||||
* - stopped: log_start never called, or previous log_stop has completed.
|
||||
* - started: log_start called, log_stop not called yet. Allocations are logged.
|
||||
* - dumping: log_stop called but not finished; samples are not logged anymore.
|
||||
*/
|
||||
prof_logging_state_t prof_logging_state = prof_logging_state_stopped;
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
static bool prof_log_dummy = false;
|
||||
#endif
|
||||
|
||||
/* Incremented for every log file that is output. */
|
||||
static uint64_t log_seq = 0;
|
||||
static char log_filename[
|
||||
/* Minimize memory bloat for non-prof builds. */
|
||||
#ifdef JEMALLOC_PROF
|
||||
PATH_MAX +
|
||||
#endif
|
||||
1];
|
||||
|
||||
/* Timestamp for most recent call to log_start(). */
|
||||
static nstime_t log_start_timestamp = NSTIME_ZERO_INITIALIZER;
|
||||
|
||||
/* Increment these when adding to the log_bt and log_thr linked lists. */
|
||||
static size_t log_bt_index = 0;
|
||||
static size_t log_thr_index = 0;
|
||||
|
||||
/* Linked list node definitions. These are only used in prof.c. */
|
||||
typedef struct prof_bt_node_s prof_bt_node_t;
|
||||
|
||||
struct prof_bt_node_s {
|
||||
prof_bt_node_t *next;
|
||||
size_t index;
|
||||
prof_bt_t bt;
|
||||
/* Variable size backtrace vector pointed to by bt. */
|
||||
void *vec[1];
|
||||
};
|
||||
|
||||
typedef struct prof_thr_node_s prof_thr_node_t;
|
||||
|
||||
struct prof_thr_node_s {
|
||||
prof_thr_node_t *next;
|
||||
size_t index;
|
||||
uint64_t thr_uid;
|
||||
/* Variable size based on thr_name_sz. */
|
||||
char name[1];
|
||||
};
|
||||
|
||||
typedef struct prof_alloc_node_s prof_alloc_node_t;
|
||||
|
||||
/* This is output when logging sampled allocations. */
|
||||
struct prof_alloc_node_s {
|
||||
prof_alloc_node_t *next;
|
||||
/* Indices into an array of thread data. */
|
||||
size_t alloc_thr_ind;
|
||||
size_t free_thr_ind;
|
||||
|
||||
/* Indices into an array of backtraces. */
|
||||
size_t alloc_bt_ind;
|
||||
size_t free_bt_ind;
|
||||
|
||||
uint64_t alloc_time_ns;
|
||||
uint64_t free_time_ns;
|
||||
|
||||
size_t usize;
|
||||
};
|
||||
|
||||
/*
|
||||
* Created on the first call to prof_log_start and deleted on prof_log_stop.
|
||||
* These are the backtraces and threads that have already been logged by an
|
||||
* allocation.
|
||||
*/
|
||||
static bool log_tables_initialized = false;
|
||||
static ckh_t log_bt_node_set;
|
||||
static ckh_t log_thr_node_set;
|
||||
|
||||
/* Store linked lists for logged data. */
|
||||
static prof_bt_node_t *log_bt_first = NULL;
|
||||
static prof_bt_node_t *log_bt_last = NULL;
|
||||
static prof_thr_node_t *log_thr_first = NULL;
|
||||
static prof_thr_node_t *log_thr_last = NULL;
|
||||
static prof_alloc_node_t *log_alloc_first = NULL;
|
||||
static prof_alloc_node_t *log_alloc_last = NULL;
|
||||
|
||||
/* Protects the prof_logging_state and any log_{...} variable. */
|
||||
static malloc_mutex_t log_mtx;
|
||||
|
||||
/*
|
||||
* Table of mutexes that are shared among gctx's. These are leaf locks, so
|
||||
* there is no problem with using them for more than one gctx at the same time.
|
||||
@ -145,6 +241,12 @@ static void prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata,
|
||||
bool even_if_attached);
|
||||
static char *prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name);
|
||||
|
||||
/* Hashtable functions for log_bt_node_set and log_thr_node_set. */
|
||||
static void prof_thr_node_hash(const void *key, size_t r_hash[2]);
|
||||
static bool prof_thr_node_keycomp(const void *k1, const void *k2);
|
||||
static void prof_bt_node_hash(const void *key, size_t r_hash[2]);
|
||||
static bool prof_bt_node_keycomp(const void *k1, const void *k2);
|
||||
|
||||
/******************************************************************************/
|
||||
/* Red-black trees. */
|
||||
|
||||
@ -242,6 +344,12 @@ prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx) {
|
||||
prof_tctx_set(tsdn, ptr, usize, NULL, tctx);
|
||||
|
||||
/* Get the current time and set this in the extent_t. We'll read this
|
||||
* when free() is called. */
|
||||
nstime_t t = NSTIME_ZERO_INITIALIZER;
|
||||
nstime_update(&t);
|
||||
prof_alloc_time_set(tsdn, ptr, NULL, t);
|
||||
|
||||
malloc_mutex_lock(tsdn, tctx->tdata->lock);
|
||||
tctx->cnts.curobjs++;
|
||||
tctx->cnts.curbytes += usize;
|
||||
@ -253,14 +361,174 @@ prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
|
||||
malloc_mutex_unlock(tsdn, tctx->tdata->lock);
|
||||
}
|
||||
|
||||
static size_t
|
||||
prof_log_bt_index(tsd_t *tsd, prof_bt_t *bt) {
|
||||
assert(prof_logging_state == prof_logging_state_started);
|
||||
malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
|
||||
|
||||
prof_bt_node_t dummy_node;
|
||||
dummy_node.bt = *bt;
|
||||
prof_bt_node_t *node;
|
||||
|
||||
/* See if this backtrace is already cached in the table. */
|
||||
if (ckh_search(&log_bt_node_set, (void *)(&dummy_node),
|
||||
(void **)(&node), NULL)) {
|
||||
size_t sz = offsetof(prof_bt_node_t, vec) +
|
||||
(bt->len * sizeof(void *));
|
||||
prof_bt_node_t *new_node = (prof_bt_node_t *)
|
||||
iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
|
||||
true, arena_get(TSDN_NULL, 0, true), true);
|
||||
if (log_bt_first == NULL) {
|
||||
log_bt_first = new_node;
|
||||
log_bt_last = new_node;
|
||||
} else {
|
||||
log_bt_last->next = new_node;
|
||||
log_bt_last = new_node;
|
||||
}
|
||||
|
||||
new_node->next = NULL;
|
||||
new_node->index = log_bt_index;
|
||||
/*
|
||||
* Copy the backtrace: bt is inside a tdata or gctx, which
|
||||
* might die before prof_log_stop is called.
|
||||
*/
|
||||
new_node->bt.len = bt->len;
|
||||
memcpy(new_node->vec, bt->vec, bt->len * sizeof(void *));
|
||||
new_node->bt.vec = new_node->vec;
|
||||
|
||||
log_bt_index++;
|
||||
ckh_insert(tsd, &log_bt_node_set, (void *)new_node, NULL);
|
||||
return new_node->index;
|
||||
} else {
|
||||
return node->index;
|
||||
}
|
||||
}
|
||||
static size_t
|
||||
prof_log_thr_index(tsd_t *tsd, uint64_t thr_uid, const char *name) {
|
||||
assert(prof_logging_state == prof_logging_state_started);
|
||||
malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
|
||||
|
||||
prof_thr_node_t dummy_node;
|
||||
dummy_node.thr_uid = thr_uid;
|
||||
prof_thr_node_t *node;
|
||||
|
||||
/* See if this thread is already cached in the table. */
|
||||
if (ckh_search(&log_thr_node_set, (void *)(&dummy_node),
|
||||
(void **)(&node), NULL)) {
|
||||
size_t sz = offsetof(prof_thr_node_t, name) + strlen(name) + 1;
|
||||
prof_thr_node_t *new_node = (prof_thr_node_t *)
|
||||
iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
|
||||
true, arena_get(TSDN_NULL, 0, true), true);
|
||||
if (log_thr_first == NULL) {
|
||||
log_thr_first = new_node;
|
||||
log_thr_last = new_node;
|
||||
} else {
|
||||
log_thr_last->next = new_node;
|
||||
log_thr_last = new_node;
|
||||
}
|
||||
|
||||
new_node->next = NULL;
|
||||
new_node->index = log_thr_index;
|
||||
new_node->thr_uid = thr_uid;
|
||||
strcpy(new_node->name, name);
|
||||
|
||||
log_thr_index++;
|
||||
ckh_insert(tsd, &log_thr_node_set, (void *)new_node, NULL);
|
||||
return new_node->index;
|
||||
} else {
|
||||
return node->index;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
prof_try_log(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx) {
|
||||
malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
|
||||
|
||||
prof_tdata_t *cons_tdata = prof_tdata_get(tsd, false);
|
||||
if (cons_tdata == NULL) {
|
||||
/*
|
||||
* We decide not to log these allocations. cons_tdata will be
|
||||
* NULL only when the current thread is in a weird state (e.g.
|
||||
* it's being destroyed).
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &log_mtx);
|
||||
|
||||
if (prof_logging_state != prof_logging_state_started) {
|
||||
goto label_done;
|
||||
}
|
||||
|
||||
if (!log_tables_initialized) {
|
||||
bool err1 = ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
|
||||
prof_bt_node_hash, prof_bt_node_keycomp);
|
||||
bool err2 = ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
|
||||
prof_thr_node_hash, prof_thr_node_keycomp);
|
||||
if (err1 || err2) {
|
||||
goto label_done;
|
||||
}
|
||||
log_tables_initialized = true;
|
||||
}
|
||||
|
||||
nstime_t alloc_time = prof_alloc_time_get(tsd_tsdn(tsd), ptr,
|
||||
(alloc_ctx_t *)NULL);
|
||||
nstime_t free_time = NSTIME_ZERO_INITIALIZER;
|
||||
nstime_update(&free_time);
|
||||
|
||||
size_t sz = sizeof(prof_alloc_node_t);
|
||||
prof_alloc_node_t *new_node = (prof_alloc_node_t *)
|
||||
iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true,
|
||||
arena_get(TSDN_NULL, 0, true), true);
|
||||
|
||||
const char *prod_thr_name = (tctx->tdata->thread_name == NULL)?
|
||||
"" : tctx->tdata->thread_name;
|
||||
const char *cons_thr_name = prof_thread_name_get(tsd);
|
||||
|
||||
prof_bt_t bt;
|
||||
/* Initialize the backtrace, using the buffer in tdata to store it. */
|
||||
bt_init(&bt, cons_tdata->vec);
|
||||
prof_backtrace(&bt);
|
||||
prof_bt_t *cons_bt = &bt;
|
||||
|
||||
/* We haven't destroyed tctx yet, so gctx should be good to read. */
|
||||
prof_bt_t *prod_bt = &tctx->gctx->bt;
|
||||
|
||||
new_node->next = NULL;
|
||||
new_node->alloc_thr_ind = prof_log_thr_index(tsd, tctx->tdata->thr_uid,
|
||||
prod_thr_name);
|
||||
new_node->free_thr_ind = prof_log_thr_index(tsd, cons_tdata->thr_uid,
|
||||
cons_thr_name);
|
||||
new_node->alloc_bt_ind = prof_log_bt_index(tsd, prod_bt);
|
||||
new_node->free_bt_ind = prof_log_bt_index(tsd, cons_bt);
|
||||
new_node->alloc_time_ns = nstime_ns(&alloc_time);
|
||||
new_node->free_time_ns = nstime_ns(&free_time);
|
||||
new_node->usize = usize;
|
||||
|
||||
if (log_alloc_first == NULL) {
|
||||
log_alloc_first = new_node;
|
||||
log_alloc_last = new_node;
|
||||
} else {
|
||||
log_alloc_last->next = new_node;
|
||||
log_alloc_last = new_node;
|
||||
}
|
||||
|
||||
label_done:
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &log_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx) {
|
||||
prof_free_sampled_object(tsd_t *tsd, const void *ptr, size_t usize,
|
||||
prof_tctx_t *tctx) {
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
|
||||
|
||||
assert(tctx->cnts.curobjs > 0);
|
||||
assert(tctx->cnts.curbytes >= usize);
|
||||
tctx->cnts.curobjs--;
|
||||
tctx->cnts.curbytes -= usize;
|
||||
|
||||
prof_try_log(tsd, ptr, usize, tctx);
|
||||
|
||||
if (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {
|
||||
prof_tctx_destroy(tsd, tctx);
|
||||
} else {
|
||||
@ -871,15 +1139,12 @@ prof_lookup(tsd_t *tsd, prof_bt_t *bt) {
|
||||
void
|
||||
prof_sample_threshold_update(prof_tdata_t *tdata) {
|
||||
#ifdef JEMALLOC_PROF
|
||||
uint64_t r;
|
||||
double u;
|
||||
|
||||
if (!config_prof) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lg_prof_sample == 0) {
|
||||
tdata->bytes_until_sample = 0;
|
||||
tsd_bytes_until_sample_set(tsd_fetch(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -901,11 +1166,16 @@ prof_sample_threshold_update(prof_tdata_t *tdata) {
|
||||
* pp 500
|
||||
* (http://luc.devroye.org/rnbookindex.html)
|
||||
*/
|
||||
r = prng_lg_range_u64(&tdata->prng_state, 53);
|
||||
u = (double)r * (1.0/9007199254740992.0L);
|
||||
tdata->bytes_until_sample = (uint64_t)(log(u) /
|
||||
uint64_t r = prng_lg_range_u64(&tdata->prng_state, 53);
|
||||
double u = (double)r * (1.0/9007199254740992.0L);
|
||||
uint64_t bytes_until_sample = (uint64_t)(log(u) /
|
||||
log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample))))
|
||||
+ (uint64_t)1U;
|
||||
if (bytes_until_sample > SSIZE_MAX) {
|
||||
bytes_until_sample = SSIZE_MAX;
|
||||
}
|
||||
tsd_bytes_until_sample_set(tsd_fetch(), bytes_until_sample);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1022,7 +1292,7 @@ prof_dump_write(bool propagate_err, const char *s) {
|
||||
}
|
||||
}
|
||||
|
||||
if (prof_dump_buf_end + slen <= PROF_DUMP_BUFSIZE) {
|
||||
if (prof_dump_buf_end + slen - i <= PROF_DUMP_BUFSIZE) {
|
||||
/* Finish writing. */
|
||||
n = slen - i;
|
||||
} else {
|
||||
@ -1033,6 +1303,7 @@ prof_dump_write(bool propagate_err, const char *s) {
|
||||
prof_dump_buf_end += n;
|
||||
i += n;
|
||||
}
|
||||
assert(i == slen);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1887,6 +2158,33 @@ prof_bt_keycomp(const void *k1, const void *k2) {
|
||||
return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
prof_bt_node_hash(const void *key, size_t r_hash[2]) {
|
||||
const prof_bt_node_t *bt_node = (prof_bt_node_t *)key;
|
||||
prof_bt_hash((void *)(&bt_node->bt), r_hash);
|
||||
}
|
||||
|
||||
static bool
|
||||
prof_bt_node_keycomp(const void *k1, const void *k2) {
|
||||
const prof_bt_node_t *bt_node1 = (prof_bt_node_t *)k1;
|
||||
const prof_bt_node_t *bt_node2 = (prof_bt_node_t *)k2;
|
||||
return prof_bt_keycomp((void *)(&bt_node1->bt),
|
||||
(void *)(&bt_node2->bt));
|
||||
}
|
||||
|
||||
static void
|
||||
prof_thr_node_hash(const void *key, size_t r_hash[2]) {
|
||||
const prof_thr_node_t *thr_node = (prof_thr_node_t *)key;
|
||||
hash(&thr_node->thr_uid, sizeof(uint64_t), 0x94122f35U, r_hash);
|
||||
}
|
||||
|
||||
static bool
|
||||
prof_thr_node_keycomp(const void *k1, const void *k2) {
|
||||
const prof_thr_node_t *thr_node1 = (prof_thr_node_t *)k1;
|
||||
const prof_thr_node_t *thr_node2 = (prof_thr_node_t *)k2;
|
||||
return thr_node1->thr_uid == thr_node2->thr_uid;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
prof_thr_uid_alloc(tsdn_t *tsdn) {
|
||||
uint64_t thr_uid;
|
||||
@ -2119,6 +2417,368 @@ prof_active_set(tsdn_t *tsdn, bool active) {
|
||||
return prof_active_old;
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
size_t
|
||||
prof_log_bt_count(void) {
|
||||
size_t cnt = 0;
|
||||
prof_bt_node_t *node = log_bt_first;
|
||||
while (node != NULL) {
|
||||
cnt++;
|
||||
node = node->next;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
size_t
|
||||
prof_log_alloc_count(void) {
|
||||
size_t cnt = 0;
|
||||
prof_alloc_node_t *node = log_alloc_first;
|
||||
while (node != NULL) {
|
||||
cnt++;
|
||||
node = node->next;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
size_t
|
||||
prof_log_thr_count(void) {
|
||||
size_t cnt = 0;
|
||||
prof_thr_node_t *node = log_thr_first;
|
||||
while (node != NULL) {
|
||||
cnt++;
|
||||
node = node->next;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
bool
|
||||
prof_log_is_logging(void) {
|
||||
return prof_logging_state == prof_logging_state_started;
|
||||
}
|
||||
|
||||
bool
|
||||
prof_log_rep_check(void) {
|
||||
if (prof_logging_state == prof_logging_state_stopped
|
||||
&& log_tables_initialized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (log_bt_last != NULL && log_bt_last->next != NULL) {
|
||||
return true;
|
||||
}
|
||||
if (log_thr_last != NULL && log_thr_last->next != NULL) {
|
||||
return true;
|
||||
}
|
||||
if (log_alloc_last != NULL && log_alloc_last->next != NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t bt_count = prof_log_bt_count();
|
||||
size_t thr_count = prof_log_thr_count();
|
||||
size_t alloc_count = prof_log_alloc_count();
|
||||
|
||||
|
||||
if (prof_logging_state == prof_logging_state_stopped) {
|
||||
if (bt_count != 0 || thr_count != 0 || alloc_count || 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
prof_alloc_node_t *node = log_alloc_first;
|
||||
while (node != NULL) {
|
||||
if (node->alloc_bt_ind >= bt_count) {
|
||||
return true;
|
||||
}
|
||||
if (node->free_bt_ind >= bt_count) {
|
||||
return true;
|
||||
}
|
||||
if (node->alloc_thr_ind >= thr_count) {
|
||||
return true;
|
||||
}
|
||||
if (node->free_thr_ind >= thr_count) {
|
||||
return true;
|
||||
}
|
||||
if (node->alloc_time_ns > node->free_time_ns) {
|
||||
return true;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
prof_log_dummy_set(bool new_value) {
|
||||
prof_log_dummy = new_value;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
prof_log_start(tsdn_t *tsdn, const char *filename) {
|
||||
if (!opt_prof || !prof_booted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
size_t buf_size = PATH_MAX + 1;
|
||||
|
||||
malloc_mutex_lock(tsdn, &log_mtx);
|
||||
|
||||
if (prof_logging_state != prof_logging_state_stopped) {
|
||||
ret = true;
|
||||
} else if (filename == NULL) {
|
||||
/* Make default name. */
|
||||
malloc_snprintf(log_filename, buf_size, "%s.%d.%"FMTu64".json",
|
||||
opt_prof_prefix, prof_getpid(), log_seq);
|
||||
log_seq++;
|
||||
prof_logging_state = prof_logging_state_started;
|
||||
} else if (strlen(filename) >= buf_size) {
|
||||
ret = true;
|
||||
} else {
|
||||
strcpy(log_filename, filename);
|
||||
prof_logging_state = prof_logging_state_started;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
nstime_update(&log_start_timestamp);
|
||||
}
|
||||
|
||||
malloc_mutex_unlock(tsdn, &log_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Used as an atexit function to stop logging on exit. */
|
||||
static void
|
||||
prof_log_stop_final(void) {
|
||||
tsd_t *tsd = tsd_fetch();
|
||||
prof_log_stop(tsd_tsdn(tsd));
|
||||
}
|
||||
|
||||
struct prof_emitter_cb_arg_s {
|
||||
int fd;
|
||||
ssize_t ret;
|
||||
};
|
||||
|
||||
static void
|
||||
prof_emitter_write_cb(void *opaque, const char *to_write) {
|
||||
struct prof_emitter_cb_arg_s *arg =
|
||||
(struct prof_emitter_cb_arg_s *)opaque;
|
||||
size_t bytes = strlen(to_write);
|
||||
#ifdef JEMALLOC_JET
|
||||
if (prof_log_dummy) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
arg->ret = write(arg->fd, (void *)to_write, bytes);
|
||||
}
|
||||
|
||||
/*
|
||||
* prof_log_emit_{...} goes through the appropriate linked list, emitting each
|
||||
* node to the json and deallocating it.
|
||||
*/
|
||||
static void
|
||||
prof_log_emit_threads(tsd_t *tsd, emitter_t *emitter) {
|
||||
emitter_json_array_kv_begin(emitter, "threads");
|
||||
prof_thr_node_t *thr_node = log_thr_first;
|
||||
prof_thr_node_t *thr_old_node;
|
||||
while (thr_node != NULL) {
|
||||
emitter_json_object_begin(emitter);
|
||||
|
||||
emitter_json_kv(emitter, "thr_uid", emitter_type_uint64,
|
||||
&thr_node->thr_uid);
|
||||
|
||||
char *thr_name = thr_node->name;
|
||||
|
||||
emitter_json_kv(emitter, "thr_name", emitter_type_string,
|
||||
&thr_name);
|
||||
|
||||
emitter_json_object_end(emitter);
|
||||
thr_old_node = thr_node;
|
||||
thr_node = thr_node->next;
|
||||
idalloc(tsd, thr_old_node);
|
||||
}
|
||||
emitter_json_array_end(emitter);
|
||||
}
|
||||
|
||||
static void
|
||||
prof_log_emit_traces(tsd_t *tsd, emitter_t *emitter) {
|
||||
emitter_json_array_kv_begin(emitter, "stack_traces");
|
||||
prof_bt_node_t *bt_node = log_bt_first;
|
||||
prof_bt_node_t *bt_old_node;
|
||||
/*
|
||||
* Calculate how many hex digits we need: twice number of bytes, two for
|
||||
* "0x", and then one more for terminating '\0'.
|
||||
*/
|
||||
char buf[2 * sizeof(intptr_t) + 3];
|
||||
size_t buf_sz = sizeof(buf);
|
||||
while (bt_node != NULL) {
|
||||
emitter_json_array_begin(emitter);
|
||||
size_t i;
|
||||
for (i = 0; i < bt_node->bt.len; i++) {
|
||||
malloc_snprintf(buf, buf_sz, "%p", bt_node->bt.vec[i]);
|
||||
char *trace_str = buf;
|
||||
emitter_json_value(emitter, emitter_type_string,
|
||||
&trace_str);
|
||||
}
|
||||
emitter_json_array_end(emitter);
|
||||
|
||||
bt_old_node = bt_node;
|
||||
bt_node = bt_node->next;
|
||||
idalloc(tsd, bt_old_node);
|
||||
}
|
||||
emitter_json_array_end(emitter);
|
||||
}
|
||||
|
||||
static void
|
||||
prof_log_emit_allocs(tsd_t *tsd, emitter_t *emitter) {
|
||||
emitter_json_array_kv_begin(emitter, "allocations");
|
||||
prof_alloc_node_t *alloc_node = log_alloc_first;
|
||||
prof_alloc_node_t *alloc_old_node;
|
||||
while (alloc_node != NULL) {
|
||||
emitter_json_object_begin(emitter);
|
||||
|
||||
emitter_json_kv(emitter, "alloc_thread", emitter_type_size,
|
||||
&alloc_node->alloc_thr_ind);
|
||||
|
||||
emitter_json_kv(emitter, "free_thread", emitter_type_size,
|
||||
&alloc_node->free_thr_ind);
|
||||
|
||||
emitter_json_kv(emitter, "alloc_trace", emitter_type_size,
|
||||
&alloc_node->alloc_bt_ind);
|
||||
|
||||
emitter_json_kv(emitter, "free_trace", emitter_type_size,
|
||||
&alloc_node->free_bt_ind);
|
||||
|
||||
emitter_json_kv(emitter, "alloc_timestamp",
|
||||
emitter_type_uint64, &alloc_node->alloc_time_ns);
|
||||
|
||||
emitter_json_kv(emitter, "free_timestamp", emitter_type_uint64,
|
||||
&alloc_node->free_time_ns);
|
||||
|
||||
emitter_json_kv(emitter, "usize", emitter_type_uint64,
|
||||
&alloc_node->usize);
|
||||
|
||||
emitter_json_object_end(emitter);
|
||||
|
||||
alloc_old_node = alloc_node;
|
||||
alloc_node = alloc_node->next;
|
||||
idalloc(tsd, alloc_old_node);
|
||||
}
|
||||
emitter_json_array_end(emitter);
|
||||
}
|
||||
|
||||
static void
|
||||
prof_log_emit_metadata(emitter_t *emitter) {
|
||||
emitter_json_object_kv_begin(emitter, "info");
|
||||
|
||||
nstime_t now = NSTIME_ZERO_INITIALIZER;
|
||||
|
||||
nstime_update(&now);
|
||||
uint64_t ns = nstime_ns(&now) - nstime_ns(&log_start_timestamp);
|
||||
emitter_json_kv(emitter, "duration", emitter_type_uint64, &ns);
|
||||
|
||||
char *vers = JEMALLOC_VERSION;
|
||||
emitter_json_kv(emitter, "version",
|
||||
emitter_type_string, &vers);
|
||||
|
||||
emitter_json_kv(emitter, "lg_sample_rate",
|
||||
emitter_type_int, &lg_prof_sample);
|
||||
|
||||
int pid = prof_getpid();
|
||||
emitter_json_kv(emitter, "pid", emitter_type_int, &pid);
|
||||
|
||||
emitter_json_object_end(emitter);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
prof_log_stop(tsdn_t *tsdn) {
|
||||
if (!opt_prof || !prof_booted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
tsd_t *tsd = tsdn_tsd(tsdn);
|
||||
malloc_mutex_lock(tsdn, &log_mtx);
|
||||
|
||||
if (prof_logging_state != prof_logging_state_started) {
|
||||
malloc_mutex_unlock(tsdn, &log_mtx);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the state to dumping. We'll set it to stopped when we're done.
|
||||
* Since other threads won't be able to start/stop/log when the state is
|
||||
* dumping, we don't have to hold the lock during the whole method.
|
||||
*/
|
||||
prof_logging_state = prof_logging_state_dumping;
|
||||
malloc_mutex_unlock(tsdn, &log_mtx);
|
||||
|
||||
|
||||
emitter_t emitter;
|
||||
|
||||
/* Create a file. */
|
||||
|
||||
int fd;
|
||||
#ifdef JEMALLOC_JET
|
||||
if (prof_log_dummy) {
|
||||
fd = 0;
|
||||
} else {
|
||||
fd = creat(log_filename, 0644);
|
||||
}
|
||||
#else
|
||||
fd = creat(log_filename, 0644);
|
||||
#endif
|
||||
|
||||
if (fd == -1) {
|
||||
malloc_printf("<jemalloc>: creat() for log file \"%s\" "
|
||||
" failed with %d\n", log_filename, errno);
|
||||
if (opt_abort) {
|
||||
abort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Emit to json. */
|
||||
struct prof_emitter_cb_arg_s arg;
|
||||
arg.fd = fd;
|
||||
emitter_init(&emitter, emitter_output_json, &prof_emitter_write_cb,
|
||||
(void *)(&arg));
|
||||
|
||||
emitter_begin(&emitter);
|
||||
prof_log_emit_metadata(&emitter);
|
||||
prof_log_emit_threads(tsd, &emitter);
|
||||
prof_log_emit_traces(tsd, &emitter);
|
||||
prof_log_emit_allocs(tsd, &emitter);
|
||||
emitter_end(&emitter);
|
||||
|
||||
/* Reset global state. */
|
||||
if (log_tables_initialized) {
|
||||
ckh_delete(tsd, &log_bt_node_set);
|
||||
ckh_delete(tsd, &log_thr_node_set);
|
||||
}
|
||||
log_tables_initialized = false;
|
||||
log_bt_index = 0;
|
||||
log_thr_index = 0;
|
||||
log_bt_first = NULL;
|
||||
log_bt_last = NULL;
|
||||
log_thr_first = NULL;
|
||||
log_thr_last = NULL;
|
||||
log_alloc_first = NULL;
|
||||
log_alloc_last = NULL;
|
||||
|
||||
malloc_mutex_lock(tsdn, &log_mtx);
|
||||
prof_logging_state = prof_logging_state_stopped;
|
||||
malloc_mutex_unlock(tsdn, &log_mtx);
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
if (prof_log_dummy) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
const char *
|
||||
prof_thread_name_get(tsd_t *tsd) {
|
||||
prof_tdata_t *tdata;
|
||||
@ -2355,6 +3015,35 @@ prof_boot2(tsd_t *tsd) {
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_prof_log) {
|
||||
prof_log_start(tsd_tsdn(tsd), NULL);
|
||||
}
|
||||
|
||||
if (atexit(prof_log_stop_final) != 0) {
|
||||
malloc_write("<jemalloc>: Error in atexit() "
|
||||
"for logging\n");
|
||||
if (opt_abort) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (malloc_mutex_init(&log_mtx, "prof_log",
|
||||
WITNESS_RANK_PROF_LOG, malloc_mutex_rank_exclusive)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
|
||||
prof_bt_node_hash, prof_bt_node_keycomp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
|
||||
prof_thr_node_hash, prof_thr_node_keycomp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
log_tables_initialized = true;
|
||||
|
||||
gctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),
|
||||
b0get(), PROF_NCTX_LOCKS * sizeof(malloc_mutex_t),
|
||||
CACHELINE);
|
||||
@ -2382,16 +3071,14 @@ prof_boot2(tsd_t *tsd) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JEMALLOC_PROF_LIBGCC
|
||||
/*
|
||||
* Cause the backtracing machinery to allocate its internal state
|
||||
* before enabling profiling.
|
||||
*/
|
||||
_Unwind_Backtrace(prof_unwind_init_callback, NULL);
|
||||
/*
|
||||
* Cause the backtracing machinery to allocate its internal
|
||||
* state before enabling profiling.
|
||||
*/
|
||||
_Unwind_Backtrace(prof_unwind_init_callback, NULL);
|
||||
#endif
|
||||
|
||||
}
|
||||
prof_booted = true;
|
||||
|
||||
return false;
|
||||
|
@ -39,7 +39,7 @@ rtree_node_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *node) {
|
||||
/* Nodes are never deleted during normal operation. */
|
||||
not_reached();
|
||||
}
|
||||
UNUSED rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc =
|
||||
rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc =
|
||||
rtree_node_dalloc_impl;
|
||||
|
||||
static rtree_leaf_elm_t *
|
||||
@ -54,7 +54,7 @@ rtree_leaf_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *leaf) {
|
||||
/* Leaves are never deleted during normal operation. */
|
||||
not_reached();
|
||||
}
|
||||
UNUSED rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc =
|
||||
rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc =
|
||||
rtree_leaf_dalloc_impl;
|
||||
|
||||
#ifdef JEMALLOC_JET
|
||||
|
24
contrib/jemalloc/src/safety_check.c
Normal file
24
contrib/jemalloc/src/safety_check.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
#include "jemalloc/internal/jemalloc_internal_includes.h"
|
||||
|
||||
static void (*safety_check_abort)(const char *message);
|
||||
|
||||
void safety_check_set_abort(void (*abort_fn)(const char *)) {
|
||||
safety_check_abort = abort_fn;
|
||||
}
|
||||
|
||||
void safety_check_fail(const char *format, ...) {
|
||||
char buf[MALLOC_PRINTF_BUFSIZE];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
malloc_vsnprintf(buf, MALLOC_PRINTF_BUFSIZE, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (safety_check_abort == NULL) {
|
||||
malloc_write(buf);
|
||||
abort();
|
||||
} else {
|
||||
safety_check_abort(buf);
|
||||
}
|
||||
}
|
313
contrib/jemalloc/src/sc.c
Normal file
313
contrib/jemalloc/src/sc.c
Normal file
@ -0,0 +1,313 @@
|
||||
#include "jemalloc/internal/jemalloc_preamble.h"
|
||||
|
||||
#include "jemalloc/internal/assert.h"
|
||||
#include "jemalloc/internal/bit_util.h"
|
||||
#include "jemalloc/internal/bitmap.h"
|
||||
#include "jemalloc/internal/pages.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/*
|
||||
* This module computes the size classes used to satisfy allocations. The logic
|
||||
* here was ported more or less line-by-line from a shell script, and because of
|
||||
* that is not the most idiomatic C. Eventually we should fix this, but for now
|
||||
* at least the damage is compartmentalized to this file.
|
||||
*/
|
||||
|
||||
sc_data_t sc_data_global;
|
||||
|
||||
static size_t
|
||||
reg_size_compute(int lg_base, int lg_delta, int ndelta) {
|
||||
return (ZU(1) << lg_base) + (ZU(ndelta) << lg_delta);
|
||||
}
|
||||
|
||||
/* Returns the number of pages in the slab. */
|
||||
static int
|
||||
slab_size(int lg_page, int lg_base, int lg_delta, int ndelta) {
|
||||
size_t page = (ZU(1) << lg_page);
|
||||
size_t reg_size = reg_size_compute(lg_base, lg_delta, ndelta);
|
||||
|
||||
size_t try_slab_size = page;
|
||||
size_t try_nregs = try_slab_size / reg_size;
|
||||
size_t perfect_slab_size = 0;
|
||||
bool perfect = false;
|
||||
/*
|
||||
* This loop continues until we find the least common multiple of the
|
||||
* page size and size class size. Size classes are all of the form
|
||||
* base + ndelta * delta == (ndelta + base/ndelta) * delta, which is
|
||||
* (ndelta + ngroup) * delta. The way we choose slabbing strategies
|
||||
* means that delta is at most the page size and ndelta < ngroup. So
|
||||
* the loop executes for at most 2 * ngroup - 1 iterations, which is
|
||||
* also the bound on the number of pages in a slab chosen by default.
|
||||
* With the current default settings, this is at most 7.
|
||||
*/
|
||||
while (!perfect) {
|
||||
perfect_slab_size = try_slab_size;
|
||||
size_t perfect_nregs = try_nregs;
|
||||
try_slab_size += page;
|
||||
try_nregs = try_slab_size / reg_size;
|
||||
if (perfect_slab_size == perfect_nregs * reg_size) {
|
||||
perfect = true;
|
||||
}
|
||||
}
|
||||
return (int)(perfect_slab_size / page);
|
||||
}
|
||||
|
||||
static void
|
||||
size_class(
|
||||
/* Output. */
|
||||
sc_t *sc,
|
||||
/* Configuration decisions. */
|
||||
int lg_max_lookup, int lg_page, int lg_ngroup,
|
||||
/* Inputs specific to the size class. */
|
||||
int index, int lg_base, int lg_delta, int ndelta) {
|
||||
sc->index = index;
|
||||
sc->lg_base = lg_base;
|
||||
sc->lg_delta = lg_delta;
|
||||
sc->ndelta = ndelta;
|
||||
sc->psz = (reg_size_compute(lg_base, lg_delta, ndelta)
|
||||
% (ZU(1) << lg_page) == 0);
|
||||
size_t size = (ZU(1) << lg_base) + (ZU(ndelta) << lg_delta);
|
||||
if (index == 0) {
|
||||
assert(!sc->psz);
|
||||
}
|
||||
if (size < (ZU(1) << (lg_page + lg_ngroup))) {
|
||||
sc->bin = true;
|
||||
sc->pgs = slab_size(lg_page, lg_base, lg_delta, ndelta);
|
||||
} else {
|
||||
sc->bin = false;
|
||||
sc->pgs = 0;
|
||||
}
|
||||
if (size <= (ZU(1) << lg_max_lookup)) {
|
||||
sc->lg_delta_lookup = lg_delta;
|
||||
} else {
|
||||
sc->lg_delta_lookup = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
size_classes(
|
||||
/* Output. */
|
||||
sc_data_t *sc_data,
|
||||
/* Determined by the system. */
|
||||
size_t lg_ptr_size, int lg_quantum,
|
||||
/* Configuration decisions. */
|
||||
int lg_tiny_min, int lg_max_lookup, int lg_page, int lg_ngroup) {
|
||||
int ptr_bits = (1 << lg_ptr_size) * 8;
|
||||
int ngroup = (1 << lg_ngroup);
|
||||
int ntiny = 0;
|
||||
int nlbins = 0;
|
||||
int lg_tiny_maxclass = (unsigned)-1;
|
||||
int nbins = 0;
|
||||
int npsizes = 0;
|
||||
|
||||
int index = 0;
|
||||
|
||||
int ndelta = 0;
|
||||
int lg_base = lg_tiny_min;
|
||||
int lg_delta = lg_base;
|
||||
|
||||
/* Outputs that we update as we go. */
|
||||
size_t lookup_maxclass = 0;
|
||||
size_t small_maxclass = 0;
|
||||
int lg_large_minclass = 0;
|
||||
size_t large_maxclass = 0;
|
||||
|
||||
/* Tiny size classes. */
|
||||
while (lg_base < lg_quantum) {
|
||||
sc_t *sc = &sc_data->sc[index];
|
||||
size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index,
|
||||
lg_base, lg_delta, ndelta);
|
||||
if (sc->lg_delta_lookup != 0) {
|
||||
nlbins = index + 1;
|
||||
}
|
||||
if (sc->psz) {
|
||||
npsizes++;
|
||||
}
|
||||
if (sc->bin) {
|
||||
nbins++;
|
||||
}
|
||||
ntiny++;
|
||||
/* Final written value is correct. */
|
||||
lg_tiny_maxclass = lg_base;
|
||||
index++;
|
||||
lg_delta = lg_base;
|
||||
lg_base++;
|
||||
}
|
||||
|
||||
/* First non-tiny (pseudo) group. */
|
||||
if (ntiny != 0) {
|
||||
sc_t *sc = &sc_data->sc[index];
|
||||
/*
|
||||
* See the note in sc.h; the first non-tiny size class has an
|
||||
* unusual encoding.
|
||||
*/
|
||||
lg_base--;
|
||||
ndelta = 1;
|
||||
size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index,
|
||||
lg_base, lg_delta, ndelta);
|
||||
index++;
|
||||
lg_base++;
|
||||
lg_delta++;
|
||||
if (sc->psz) {
|
||||
npsizes++;
|
||||
}
|
||||
if (sc->bin) {
|
||||
nbins++;
|
||||
}
|
||||
}
|
||||
while (ndelta < ngroup) {
|
||||
sc_t *sc = &sc_data->sc[index];
|
||||
size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index,
|
||||
lg_base, lg_delta, ndelta);
|
||||
index++;
|
||||
ndelta++;
|
||||
if (sc->psz) {
|
||||
npsizes++;
|
||||
}
|
||||
if (sc->bin) {
|
||||
nbins++;
|
||||
}
|
||||
}
|
||||
|
||||
/* All remaining groups. */
|
||||
lg_base = lg_base + lg_ngroup;
|
||||
while (lg_base < ptr_bits - 1) {
|
||||
ndelta = 1;
|
||||
int ndelta_limit;
|
||||
if (lg_base == ptr_bits - 2) {
|
||||
ndelta_limit = ngroup - 1;
|
||||
} else {
|
||||
ndelta_limit = ngroup;
|
||||
}
|
||||
while (ndelta <= ndelta_limit) {
|
||||
sc_t *sc = &sc_data->sc[index];
|
||||
size_class(sc, lg_max_lookup, lg_page, lg_ngroup, index,
|
||||
lg_base, lg_delta, ndelta);
|
||||
if (sc->lg_delta_lookup != 0) {
|
||||
nlbins = index + 1;
|
||||
/* Final written value is correct. */
|
||||
lookup_maxclass = (ZU(1) << lg_base)
|
||||
+ (ZU(ndelta) << lg_delta);
|
||||
}
|
||||
if (sc->psz) {
|
||||
npsizes++;
|
||||
}
|
||||
if (sc->bin) {
|
||||
nbins++;
|
||||
/* Final written value is correct. */
|
||||
small_maxclass = (ZU(1) << lg_base)
|
||||
+ (ZU(ndelta) << lg_delta);
|
||||
if (lg_ngroup > 0) {
|
||||
lg_large_minclass = lg_base + 1;
|
||||
} else {
|
||||
lg_large_minclass = lg_base + 2;
|
||||
}
|
||||
}
|
||||
large_maxclass = (ZU(1) << lg_base)
|
||||
+ (ZU(ndelta) << lg_delta);
|
||||
index++;
|
||||
ndelta++;
|
||||
}
|
||||
lg_base++;
|
||||
lg_delta++;
|
||||
}
|
||||
/* Additional outputs. */
|
||||
int nsizes = index;
|
||||
unsigned lg_ceil_nsizes = lg_ceil(nsizes);
|
||||
|
||||
/* Fill in the output data. */
|
||||
sc_data->ntiny = ntiny;
|
||||
sc_data->nlbins = nlbins;
|
||||
sc_data->nbins = nbins;
|
||||
sc_data->nsizes = nsizes;
|
||||
sc_data->lg_ceil_nsizes = lg_ceil_nsizes;
|
||||
sc_data->npsizes = npsizes;
|
||||
sc_data->lg_tiny_maxclass = lg_tiny_maxclass;
|
||||
sc_data->lookup_maxclass = lookup_maxclass;
|
||||
sc_data->small_maxclass = small_maxclass;
|
||||
sc_data->lg_large_minclass = lg_large_minclass;
|
||||
sc_data->large_minclass = (ZU(1) << lg_large_minclass);
|
||||
sc_data->large_maxclass = large_maxclass;
|
||||
|
||||
/*
|
||||
* We compute these values in two ways:
|
||||
* - Incrementally, as above.
|
||||
* - In macros, in sc.h.
|
||||
* The computation is easier when done incrementally, but putting it in
|
||||
* a constant makes it available to the fast paths without having to
|
||||
* touch the extra global cacheline. We assert, however, that the two
|
||||
* computations are equivalent.
|
||||
*/
|
||||
assert(sc_data->npsizes == SC_NPSIZES);
|
||||
assert(sc_data->lg_tiny_maxclass == SC_LG_TINY_MAXCLASS);
|
||||
assert(sc_data->small_maxclass == SC_SMALL_MAXCLASS);
|
||||
assert(sc_data->large_minclass == SC_LARGE_MINCLASS);
|
||||
assert(sc_data->lg_large_minclass == SC_LG_LARGE_MINCLASS);
|
||||
assert(sc_data->large_maxclass == SC_LARGE_MAXCLASS);
|
||||
|
||||
/*
|
||||
* In the allocation fastpath, we want to assume that we can
|
||||
* unconditionally subtract the requested allocation size from
|
||||
* a ssize_t, and detect passing through 0 correctly. This
|
||||
* results in optimal generated code. For this to work, the
|
||||
* maximum allocation size must be less than SSIZE_MAX.
|
||||
*/
|
||||
assert(SC_LARGE_MAXCLASS < SSIZE_MAX);
|
||||
}
|
||||
|
||||
void
|
||||
sc_data_init(sc_data_t *sc_data) {
|
||||
assert(!sc_data->initialized);
|
||||
|
||||
int lg_max_lookup = 12;
|
||||
|
||||
size_classes(sc_data, LG_SIZEOF_PTR, LG_QUANTUM, SC_LG_TINY_MIN,
|
||||
lg_max_lookup, LG_PAGE, 2);
|
||||
|
||||
sc_data->initialized = true;
|
||||
}
|
||||
|
||||
static void
|
||||
sc_data_update_sc_slab_size(sc_t *sc, size_t reg_size, size_t pgs_guess) {
|
||||
size_t min_pgs = reg_size / PAGE;
|
||||
if (reg_size % PAGE != 0) {
|
||||
min_pgs++;
|
||||
}
|
||||
/*
|
||||
* BITMAP_MAXBITS is actually determined by putting the smallest
|
||||
* possible size-class on one page, so this can never be 0.
|
||||
*/
|
||||
size_t max_pgs = BITMAP_MAXBITS * reg_size / PAGE;
|
||||
|
||||
assert(min_pgs <= max_pgs);
|
||||
assert(min_pgs > 0);
|
||||
assert(max_pgs >= 1);
|
||||
if (pgs_guess < min_pgs) {
|
||||
sc->pgs = (int)min_pgs;
|
||||
} else if (pgs_guess > max_pgs) {
|
||||
sc->pgs = (int)max_pgs;
|
||||
} else {
|
||||
sc->pgs = (int)pgs_guess;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_data_update_slab_size(sc_data_t *data, size_t begin, size_t end, int pgs) {
|
||||
assert(data->initialized);
|
||||
for (int i = 0; i < data->nsizes; i++) {
|
||||
sc_t *sc = &data->sc[i];
|
||||
if (!sc->bin) {
|
||||
break;
|
||||
}
|
||||
size_t reg_size = reg_size_compute(sc->lg_base, sc->lg_delta,
|
||||
sc->ndelta);
|
||||
if (begin <= reg_size && reg_size <= end) {
|
||||
sc_data_update_sc_slab_size(sc, reg_size, pgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sc_boot(sc_data_t *data) {
|
||||
sc_data_init(data);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -2,106 +2,63 @@
|
||||
#include "jemalloc/internal/sz.h"
|
||||
|
||||
JEMALLOC_ALIGNED(CACHELINE)
|
||||
const size_t sz_pind2sz_tab[NPSIZES+1] = {
|
||||
#define PSZ_yes(lg_grp, ndelta, lg_delta) \
|
||||
(((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta))),
|
||||
#define PSZ_no(lg_grp, ndelta, lg_delta)
|
||||
#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \
|
||||
PSZ_##psz(lg_grp, ndelta, lg_delta)
|
||||
SIZE_CLASSES
|
||||
#undef PSZ_yes
|
||||
#undef PSZ_no
|
||||
#undef SC
|
||||
(LARGE_MAXCLASS + PAGE)
|
||||
};
|
||||
size_t sz_pind2sz_tab[SC_NPSIZES+1];
|
||||
|
||||
static void
|
||||
sz_boot_pind2sz_tab(const sc_data_t *sc_data) {
|
||||
int pind = 0;
|
||||
for (unsigned i = 0; i < SC_NSIZES; i++) {
|
||||
const sc_t *sc = &sc_data->sc[i];
|
||||
if (sc->psz) {
|
||||
sz_pind2sz_tab[pind] = (ZU(1) << sc->lg_base)
|
||||
+ (ZU(sc->ndelta) << sc->lg_delta);
|
||||
pind++;
|
||||
}
|
||||
}
|
||||
for (int i = pind; i <= (int)SC_NPSIZES; i++) {
|
||||
sz_pind2sz_tab[pind] = sc_data->large_maxclass + PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALIGNED(CACHELINE)
|
||||
const size_t sz_index2size_tab[NSIZES] = {
|
||||
#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \
|
||||
((ZU(1)<<lg_grp) + (ZU(ndelta)<<lg_delta)),
|
||||
SIZE_CLASSES
|
||||
#undef SC
|
||||
};
|
||||
size_t sz_index2size_tab[SC_NSIZES];
|
||||
|
||||
static void
|
||||
sz_boot_index2size_tab(const sc_data_t *sc_data) {
|
||||
for (unsigned i = 0; i < SC_NSIZES; i++) {
|
||||
const sc_t *sc = &sc_data->sc[i];
|
||||
sz_index2size_tab[i] = (ZU(1) << sc->lg_base)
|
||||
+ (ZU(sc->ndelta) << (sc->lg_delta));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* To keep this table small, we divide sizes by the tiny min size, which gives
|
||||
* the smallest interval for which the result can change.
|
||||
*/
|
||||
JEMALLOC_ALIGNED(CACHELINE)
|
||||
const uint8_t sz_size2index_tab[] = {
|
||||
#if LG_TINY_MIN == 0
|
||||
/* The div module doesn't support division by 1. */
|
||||
#error "Unsupported LG_TINY_MIN"
|
||||
#define S2B_0(i) i,
|
||||
#elif LG_TINY_MIN == 1
|
||||
#warning "Dangerous LG_TINY_MIN"
|
||||
#define S2B_1(i) i,
|
||||
#elif LG_TINY_MIN == 2
|
||||
#warning "Dangerous LG_TINY_MIN"
|
||||
#define S2B_2(i) i,
|
||||
#elif LG_TINY_MIN == 3
|
||||
#define S2B_3(i) i,
|
||||
#elif LG_TINY_MIN == 4
|
||||
#define S2B_4(i) i,
|
||||
#elif LG_TINY_MIN == 5
|
||||
#define S2B_5(i) i,
|
||||
#elif LG_TINY_MIN == 6
|
||||
#define S2B_6(i) i,
|
||||
#elif LG_TINY_MIN == 7
|
||||
#define S2B_7(i) i,
|
||||
#elif LG_TINY_MIN == 8
|
||||
#define S2B_8(i) i,
|
||||
#elif LG_TINY_MIN == 9
|
||||
#define S2B_9(i) i,
|
||||
#elif LG_TINY_MIN == 10
|
||||
#define S2B_10(i) i,
|
||||
#elif LG_TINY_MIN == 11
|
||||
#define S2B_11(i) i,
|
||||
#else
|
||||
#error "Unsupported LG_TINY_MIN"
|
||||
#endif
|
||||
#if LG_TINY_MIN < 1
|
||||
#define S2B_1(i) S2B_0(i) S2B_0(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 2
|
||||
#define S2B_2(i) S2B_1(i) S2B_1(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 3
|
||||
#define S2B_3(i) S2B_2(i) S2B_2(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 4
|
||||
#define S2B_4(i) S2B_3(i) S2B_3(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 5
|
||||
#define S2B_5(i) S2B_4(i) S2B_4(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 6
|
||||
#define S2B_6(i) S2B_5(i) S2B_5(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 7
|
||||
#define S2B_7(i) S2B_6(i) S2B_6(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 8
|
||||
#define S2B_8(i) S2B_7(i) S2B_7(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 9
|
||||
#define S2B_9(i) S2B_8(i) S2B_8(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 10
|
||||
#define S2B_10(i) S2B_9(i) S2B_9(i)
|
||||
#endif
|
||||
#if LG_TINY_MIN < 11
|
||||
#define S2B_11(i) S2B_10(i) S2B_10(i)
|
||||
#endif
|
||||
#define S2B_no(i)
|
||||
#define SC(index, lg_grp, lg_delta, ndelta, psz, bin, pgs, lg_delta_lookup) \
|
||||
S2B_##lg_delta_lookup(index)
|
||||
SIZE_CLASSES
|
||||
#undef S2B_3
|
||||
#undef S2B_4
|
||||
#undef S2B_5
|
||||
#undef S2B_6
|
||||
#undef S2B_7
|
||||
#undef S2B_8
|
||||
#undef S2B_9
|
||||
#undef S2B_10
|
||||
#undef S2B_11
|
||||
#undef S2B_no
|
||||
#undef SC
|
||||
};
|
||||
uint8_t sz_size2index_tab[(SC_LOOKUP_MAXCLASS >> SC_LG_TINY_MIN) + 1];
|
||||
|
||||
static void
|
||||
sz_boot_size2index_tab(const sc_data_t *sc_data) {
|
||||
size_t dst_max = (SC_LOOKUP_MAXCLASS >> SC_LG_TINY_MIN) + 1;
|
||||
size_t dst_ind = 0;
|
||||
for (unsigned sc_ind = 0; sc_ind < SC_NSIZES && dst_ind < dst_max;
|
||||
sc_ind++) {
|
||||
const sc_t *sc = &sc_data->sc[sc_ind];
|
||||
size_t sz = (ZU(1) << sc->lg_base)
|
||||
+ (ZU(sc->ndelta) << sc->lg_delta);
|
||||
size_t max_ind = ((sz + (ZU(1) << SC_LG_TINY_MIN) - 1)
|
||||
>> SC_LG_TINY_MIN);
|
||||
for (; dst_ind <= max_ind && dst_ind < dst_max; dst_ind++) {
|
||||
sz_size2index_tab[dst_ind] = sc_ind;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sz_boot(const sc_data_t *sc_data) {
|
||||
sz_boot_pind2sz_tab(sc_data);
|
||||
sz_boot_index2size_tab(sc_data);
|
||||
sz_boot_size2index_tab(sc_data);
|
||||
}
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
#include "jemalloc/internal/assert.h"
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/size_classes.h"
|
||||
#include "jemalloc/internal/safety_check.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
@ -41,7 +42,7 @@ tcache_event_hard(tsd_t *tsd, tcache_t *tcache) {
|
||||
szind_t binind = tcache->next_gc_bin;
|
||||
|
||||
cache_bin_t *tbin;
|
||||
if (binind < NBINS) {
|
||||
if (binind < SC_NBINS) {
|
||||
tbin = tcache_small_bin_get(tcache, binind);
|
||||
} else {
|
||||
tbin = tcache_large_bin_get(tcache, binind);
|
||||
@ -50,7 +51,7 @@ tcache_event_hard(tsd_t *tsd, tcache_t *tcache) {
|
||||
/*
|
||||
* Flush (ceiling) 3/4 of the objects below the low water mark.
|
||||
*/
|
||||
if (binind < NBINS) {
|
||||
if (binind < SC_NBINS) {
|
||||
tcache_bin_flush_small(tsd, tcache, tbin, binind,
|
||||
tbin->ncached - tbin->low_water + (tbin->low_water
|
||||
>> 2));
|
||||
@ -72,7 +73,7 @@ tcache_event_hard(tsd_t *tsd, tcache_t *tcache) {
|
||||
* Increase fill count by 2X for small bins. Make sure
|
||||
* lg_fill_div stays greater than 0.
|
||||
*/
|
||||
if (binind < NBINS && tcache->lg_fill_div[binind] > 1) {
|
||||
if (binind < SC_NBINS && tcache->lg_fill_div[binind] > 1) {
|
||||
tcache->lg_fill_div[binind]--;
|
||||
}
|
||||
}
|
||||
@ -100,28 +101,67 @@ tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enabled with --enable-extra-size-check. */
|
||||
static void
|
||||
tbin_extents_lookup_size_check(tsdn_t *tsdn, cache_bin_t *tbin, szind_t binind,
|
||||
size_t nflush, extent_t **extents){
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
|
||||
/*
|
||||
* Verify that the items in the tcache all have the correct size; this
|
||||
* is useful for catching sized deallocation bugs, also to fail early
|
||||
* instead of corrupting metadata. Since this can be turned on for opt
|
||||
* builds, avoid the branch in the loop.
|
||||
*/
|
||||
szind_t szind;
|
||||
size_t sz_sum = binind * nflush;
|
||||
for (unsigned i = 0 ; i < nflush; i++) {
|
||||
rtree_extent_szind_read(tsdn, &extents_rtree,
|
||||
rtree_ctx, (uintptr_t)*(tbin->avail - 1 - i), true,
|
||||
&extents[i], &szind);
|
||||
sz_sum -= szind;
|
||||
}
|
||||
if (sz_sum != 0) {
|
||||
safety_check_fail("<jemalloc>: size mismatch in thread cache "
|
||||
"detected, likely caused by sized deallocation bugs by "
|
||||
"application. Abort.\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
|
||||
szind_t binind, unsigned rem) {
|
||||
bool merged_stats = false;
|
||||
|
||||
assert(binind < NBINS);
|
||||
assert(binind < SC_NBINS);
|
||||
assert((cache_bin_sz_t)rem <= tbin->ncached);
|
||||
|
||||
arena_t *arena = tcache->arena;
|
||||
assert(arena != NULL);
|
||||
unsigned nflush = tbin->ncached - rem;
|
||||
VARIABLE_ARRAY(extent_t *, item_extent, nflush);
|
||||
/* Look up extent once per item. */
|
||||
for (unsigned i = 0 ; i < nflush; i++) {
|
||||
item_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i));
|
||||
}
|
||||
|
||||
/* Look up extent once per item. */
|
||||
if (config_opt_safety_checks) {
|
||||
tbin_extents_lookup_size_check(tsd_tsdn(tsd), tbin, binind,
|
||||
nflush, item_extent);
|
||||
} else {
|
||||
for (unsigned i = 0 ; i < nflush; i++) {
|
||||
item_extent[i] = iealloc(tsd_tsdn(tsd),
|
||||
*(tbin->avail - 1 - i));
|
||||
}
|
||||
}
|
||||
while (nflush > 0) {
|
||||
/* Lock the arena bin associated with the first object. */
|
||||
extent_t *extent = item_extent[0];
|
||||
arena_t *bin_arena = extent_arena_get(extent);
|
||||
bin_t *bin = &bin_arena->bins[binind];
|
||||
unsigned bin_arena_ind = extent_arena_ind_get(extent);
|
||||
arena_t *bin_arena = arena_get(tsd_tsdn(tsd), bin_arena_ind,
|
||||
false);
|
||||
unsigned binshard = extent_binshard_get(extent);
|
||||
assert(binshard < bin_infos[binind].n_shards);
|
||||
bin_t *bin = &bin_arena->bins[binind].bin_shards[binshard];
|
||||
|
||||
if (config_prof && bin_arena == arena) {
|
||||
if (arena_prof_accum(tsd_tsdn(tsd), arena,
|
||||
@ -132,8 +172,7 @@ tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
|
||||
}
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
if (config_stats && bin_arena == arena) {
|
||||
assert(!merged_stats);
|
||||
if (config_stats && bin_arena == arena && !merged_stats) {
|
||||
merged_stats = true;
|
||||
bin->stats.nflushes++;
|
||||
bin->stats.nrequests += tbin->tstats.nrequests;
|
||||
@ -145,9 +184,10 @@ tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
|
||||
extent = item_extent[i];
|
||||
assert(ptr != NULL && extent != NULL);
|
||||
|
||||
if (extent_arena_get(extent) == bin_arena) {
|
||||
if (extent_arena_ind_get(extent) == bin_arena_ind
|
||||
&& extent_binshard_get(extent) == binshard) {
|
||||
arena_dalloc_bin_junked_locked(tsd_tsdn(tsd),
|
||||
bin_arena, extent, ptr);
|
||||
bin_arena, bin, binind, extent, ptr);
|
||||
} else {
|
||||
/*
|
||||
* This object was allocated via a different
|
||||
@ -169,8 +209,9 @@ tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
|
||||
* The flush loop didn't happen to flush to this thread's
|
||||
* arena, so the stats didn't get merged. Manually do so now.
|
||||
*/
|
||||
bin_t *bin = &arena->bins[binind];
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
|
||||
unsigned binshard;
|
||||
bin_t *bin = arena_bin_choose_lock(tsd_tsdn(tsd), arena, binind,
|
||||
&binshard);
|
||||
bin->stats.nflushes++;
|
||||
bin->stats.nrequests += tbin->tstats.nrequests;
|
||||
tbin->tstats.nrequests = 0;
|
||||
@ -193,50 +234,63 @@ tcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,
|
||||
assert(binind < nhbins);
|
||||
assert((cache_bin_sz_t)rem <= tbin->ncached);
|
||||
|
||||
arena_t *arena = tcache->arena;
|
||||
assert(arena != NULL);
|
||||
arena_t *tcache_arena = tcache->arena;
|
||||
assert(tcache_arena != NULL);
|
||||
unsigned nflush = tbin->ncached - rem;
|
||||
VARIABLE_ARRAY(extent_t *, item_extent, nflush);
|
||||
|
||||
#ifndef JEMALLOC_EXTRA_SIZE_CHECK
|
||||
/* Look up extent once per item. */
|
||||
for (unsigned i = 0 ; i < nflush; i++) {
|
||||
item_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i));
|
||||
}
|
||||
|
||||
#else
|
||||
tbin_extents_lookup_size_check(tsd_tsdn(tsd), tbin, binind, nflush,
|
||||
item_extent);
|
||||
#endif
|
||||
while (nflush > 0) {
|
||||
/* Lock the arena associated with the first object. */
|
||||
extent_t *extent = item_extent[0];
|
||||
arena_t *locked_arena = extent_arena_get(extent);
|
||||
UNUSED bool idump;
|
||||
unsigned locked_arena_ind = extent_arena_ind_get(extent);
|
||||
arena_t *locked_arena = arena_get(tsd_tsdn(tsd),
|
||||
locked_arena_ind, false);
|
||||
bool idump;
|
||||
|
||||
if (config_prof) {
|
||||
idump = false;
|
||||
}
|
||||
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->large_mtx);
|
||||
bool lock_large = !arena_is_auto(locked_arena);
|
||||
if (lock_large) {
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->large_mtx);
|
||||
}
|
||||
for (unsigned i = 0; i < nflush; i++) {
|
||||
void *ptr = *(tbin->avail - 1 - i);
|
||||
assert(ptr != NULL);
|
||||
extent = item_extent[i];
|
||||
if (extent_arena_get(extent) == locked_arena) {
|
||||
if (extent_arena_ind_get(extent) == locked_arena_ind) {
|
||||
large_dalloc_prep_junked_locked(tsd_tsdn(tsd),
|
||||
extent);
|
||||
}
|
||||
}
|
||||
if ((config_prof || config_stats) && locked_arena == arena) {
|
||||
if ((config_prof || config_stats) &&
|
||||
(locked_arena == tcache_arena)) {
|
||||
if (config_prof) {
|
||||
idump = arena_prof_accum(tsd_tsdn(tsd), arena,
|
||||
tcache->prof_accumbytes);
|
||||
idump = arena_prof_accum(tsd_tsdn(tsd),
|
||||
tcache_arena, tcache->prof_accumbytes);
|
||||
tcache->prof_accumbytes = 0;
|
||||
}
|
||||
if (config_stats) {
|
||||
merged_stats = true;
|
||||
arena_stats_large_nrequests_add(tsd_tsdn(tsd),
|
||||
&arena->stats, binind,
|
||||
arena_stats_large_flush_nrequests_add(
|
||||
tsd_tsdn(tsd), &tcache_arena->stats, binind,
|
||||
tbin->tstats.nrequests);
|
||||
tbin->tstats.nrequests = 0;
|
||||
}
|
||||
}
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->large_mtx);
|
||||
if (lock_large) {
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->large_mtx);
|
||||
}
|
||||
|
||||
unsigned ndeferred = 0;
|
||||
for (unsigned i = 0; i < nflush; i++) {
|
||||
@ -244,7 +298,7 @@ tcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,
|
||||
extent = item_extent[i];
|
||||
assert(ptr != NULL && extent != NULL);
|
||||
|
||||
if (extent_arena_get(extent) == locked_arena) {
|
||||
if (extent_arena_ind_get(extent) == locked_arena_ind) {
|
||||
large_dalloc_finish(tsd_tsdn(tsd), extent);
|
||||
} else {
|
||||
/*
|
||||
@ -270,8 +324,8 @@ tcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,
|
||||
* The flush loop didn't happen to flush to this thread's
|
||||
* arena, so the stats didn't get merged. Manually do so now.
|
||||
*/
|
||||
arena_stats_large_nrequests_add(tsd_tsdn(tsd), &arena->stats,
|
||||
binind, tbin->tstats.nrequests);
|
||||
arena_stats_large_flush_nrequests_add(tsd_tsdn(tsd),
|
||||
&tcache_arena->stats, binind, tbin->tstats.nrequests);
|
||||
tbin->tstats.nrequests = 0;
|
||||
}
|
||||
|
||||
@ -363,10 +417,10 @@ tcache_init(tsd_t *tsd, tcache_t *tcache, void *avail_stack) {
|
||||
|
||||
size_t stack_offset = 0;
|
||||
assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0);
|
||||
memset(tcache->bins_small, 0, sizeof(cache_bin_t) * NBINS);
|
||||
memset(tcache->bins_large, 0, sizeof(cache_bin_t) * (nhbins - NBINS));
|
||||
memset(tcache->bins_small, 0, sizeof(cache_bin_t) * SC_NBINS);
|
||||
memset(tcache->bins_large, 0, sizeof(cache_bin_t) * (nhbins - SC_NBINS));
|
||||
unsigned i = 0;
|
||||
for (; i < NBINS; i++) {
|
||||
for (; i < SC_NBINS; i++) {
|
||||
tcache->lg_fill_div[i] = 1;
|
||||
stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);
|
||||
/*
|
||||
@ -458,7 +512,7 @@ static void
|
||||
tcache_flush_cache(tsd_t *tsd, tcache_t *tcache) {
|
||||
assert(tcache->arena != NULL);
|
||||
|
||||
for (unsigned i = 0; i < NBINS; i++) {
|
||||
for (unsigned i = 0; i < SC_NBINS; i++) {
|
||||
cache_bin_t *tbin = tcache_small_bin_get(tcache, i);
|
||||
tcache_bin_flush_small(tsd, tcache, tbin, i, 0);
|
||||
|
||||
@ -466,7 +520,7 @@ tcache_flush_cache(tsd_t *tsd, tcache_t *tcache) {
|
||||
assert(tbin->tstats.nrequests == 0);
|
||||
}
|
||||
}
|
||||
for (unsigned i = NBINS; i < nhbins; i++) {
|
||||
for (unsigned i = SC_NBINS; i < nhbins; i++) {
|
||||
cache_bin_t *tbin = tcache_large_bin_get(tcache, i);
|
||||
tcache_bin_flush_large(tsd, tbin, i, 0, tcache);
|
||||
|
||||
@ -491,6 +545,7 @@ tcache_flush(tsd_t *tsd) {
|
||||
static void
|
||||
tcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) {
|
||||
tcache_flush_cache(tsd, tcache);
|
||||
arena_t *arena = tcache->arena;
|
||||
tcache_arena_dissociate(tsd_tsdn(tsd), tcache);
|
||||
|
||||
if (tsd_tcache) {
|
||||
@ -503,6 +558,23 @@ tcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) {
|
||||
/* Release both the tcache struct and avail array. */
|
||||
idalloctm(tsd_tsdn(tsd), tcache, NULL, NULL, true, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* The deallocation and tcache flush above may not trigger decay since
|
||||
* we are on the tcache shutdown path (potentially with non-nominal
|
||||
* tsd). Manually trigger decay to avoid pathological cases. Also
|
||||
* include arena 0 because the tcache array is allocated from it.
|
||||
*/
|
||||
arena_decay(tsd_tsdn(tsd), arena_get(tsd_tsdn(tsd), 0, false),
|
||||
false, false);
|
||||
|
||||
if (arena_nthreads_get(arena, false) == 0 &&
|
||||
!background_thread_enabled()) {
|
||||
/* Force purging when no threads assigned to the arena anymore. */
|
||||
arena_decay(tsd_tsdn(tsd), arena, false, true);
|
||||
} else {
|
||||
arena_decay(tsd_tsdn(tsd), arena, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* For auto tcache (embedded in TSD) only. */
|
||||
@ -532,10 +604,10 @@ tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {
|
||||
cassert(config_stats);
|
||||
|
||||
/* Merge and reset tcache stats. */
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
bin_t *bin = &arena->bins[i];
|
||||
for (i = 0; i < SC_NBINS; i++) {
|
||||
cache_bin_t *tbin = tcache_small_bin_get(tcache, i);
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
unsigned binshard;
|
||||
bin_t *bin = arena_bin_choose_lock(tsdn, arena, i, &binshard);
|
||||
bin->stats.nrequests += tbin->tstats.nrequests;
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
tbin->tstats.nrequests = 0;
|
||||
@ -543,7 +615,7 @@ tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {
|
||||
|
||||
for (; i < nhbins; i++) {
|
||||
cache_bin_t *tbin = tcache_large_bin_get(tcache, i);
|
||||
arena_stats_large_nrequests_add(tsdn, &arena->stats, i,
|
||||
arena_stats_large_flush_nrequests_add(tsdn, &arena->stats, i,
|
||||
tbin->tstats.nrequests);
|
||||
tbin->tstats.nrequests = 0;
|
||||
}
|
||||
@ -614,23 +686,32 @@ tcaches_create(tsd_t *tsd, unsigned *r_ind) {
|
||||
}
|
||||
|
||||
static tcache_t *
|
||||
tcaches_elm_remove(tsd_t *tsd, tcaches_t *elm) {
|
||||
tcaches_elm_remove(tsd_t *tsd, tcaches_t *elm, bool allow_reinit) {
|
||||
malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx);
|
||||
|
||||
if (elm->tcache == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
tcache_t *tcache = elm->tcache;
|
||||
elm->tcache = NULL;
|
||||
if (allow_reinit) {
|
||||
elm->tcache = TCACHES_ELM_NEED_REINIT;
|
||||
} else {
|
||||
elm->tcache = NULL;
|
||||
}
|
||||
|
||||
if (tcache == TCACHES_ELM_NEED_REINIT) {
|
||||
return NULL;
|
||||
}
|
||||
return tcache;
|
||||
}
|
||||
|
||||
void
|
||||
tcaches_flush(tsd_t *tsd, unsigned ind) {
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);
|
||||
tcache_t *tcache = tcaches_elm_remove(tsd, &tcaches[ind]);
|
||||
tcache_t *tcache = tcaches_elm_remove(tsd, &tcaches[ind], true);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
|
||||
if (tcache != NULL) {
|
||||
/* Destroy the tcache; recreate in tcaches_get() if needed. */
|
||||
tcache_destroy(tsd, tcache, false);
|
||||
}
|
||||
}
|
||||
@ -639,7 +720,7 @@ void
|
||||
tcaches_destroy(tsd_t *tsd, unsigned ind) {
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);
|
||||
tcaches_t *elm = &tcaches[ind];
|
||||
tcache_t *tcache = tcaches_elm_remove(tsd, elm);
|
||||
tcache_t *tcache = tcaches_elm_remove(tsd, elm, false);
|
||||
elm->next = tcaches_avail;
|
||||
tcaches_avail = elm;
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
|
||||
@ -652,8 +733,8 @@ bool
|
||||
tcache_boot(tsdn_t *tsdn) {
|
||||
/* If necessary, clamp opt_lg_tcache_max. */
|
||||
if (opt_lg_tcache_max < 0 || (ZU(1) << opt_lg_tcache_max) <
|
||||
SMALL_MAXCLASS) {
|
||||
tcache_maxclass = SMALL_MAXCLASS;
|
||||
SC_SMALL_MAXCLASS) {
|
||||
tcache_maxclass = SC_SMALL_MAXCLASS;
|
||||
} else {
|
||||
tcache_maxclass = (ZU(1) << opt_lg_tcache_max);
|
||||
}
|
||||
@ -673,7 +754,7 @@ tcache_boot(tsdn_t *tsdn) {
|
||||
}
|
||||
stack_nelms = 0;
|
||||
unsigned i;
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
for (i = 0; i < SC_NBINS; i++) {
|
||||
if ((bin_infos[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) {
|
||||
tcache_bin_info[i].ncached_max =
|
||||
TCACHE_NSLOTS_SMALL_MIN;
|
||||
|
@ -6,7 +6,7 @@
|
||||
* from outside the generated library, so that we can use them in test code.
|
||||
*/
|
||||
JEMALLOC_EXPORT
|
||||
void (*hooks_arena_new_hook)() = NULL;
|
||||
void (*test_hooks_arena_new_hook)() = NULL;
|
||||
|
||||
JEMALLOC_EXPORT
|
||||
void (*hooks_libc_hook)() = NULL;
|
||||
void (*test_hooks_libc_hook)() = NULL;
|
@ -12,12 +12,16 @@
|
||||
static unsigned ncleanups;
|
||||
static malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX];
|
||||
|
||||
/* TSD_INITIALIZER triggers "-Wmissing-field-initializer" */
|
||||
JEMALLOC_DIAGNOSTIC_PUSH
|
||||
JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
|
||||
|
||||
#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
|
||||
__thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;
|
||||
__thread bool JEMALLOC_TLS_MODEL tsd_initialized = false;
|
||||
JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls = TSD_INITIALIZER;
|
||||
JEMALLOC_TSD_TYPE_ATTR(bool) JEMALLOC_TLS_MODEL tsd_initialized = false;
|
||||
bool tsd_booted = false;
|
||||
#elif (defined(JEMALLOC_TLS))
|
||||
__thread tsd_t JEMALLOC_TLS_MODEL tsd_tls = TSD_INITIALIZER;
|
||||
JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls = TSD_INITIALIZER;
|
||||
pthread_key_t tsd_tsd;
|
||||
bool tsd_booted = false;
|
||||
#elif (defined(_WIN32))
|
||||
@ -41,6 +45,7 @@ tsd_init_head_t tsd_init_head = {
|
||||
ql_head_initializer(blocks),
|
||||
MALLOC_MUTEX_INITIALIZER
|
||||
};
|
||||
|
||||
tsd_wrapper_t tsd_boot_wrapper = {
|
||||
false,
|
||||
TSD_INITIALIZER
|
||||
@ -48,17 +53,164 @@ tsd_wrapper_t tsd_boot_wrapper = {
|
||||
bool tsd_booted = false;
|
||||
#endif
|
||||
|
||||
JEMALLOC_DIAGNOSTIC_POP
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* A list of all the tsds in the nominal state. */
|
||||
typedef ql_head(tsd_t) tsd_list_t;
|
||||
static tsd_list_t tsd_nominal_tsds = ql_head_initializer(tsd_nominal_tsds);
|
||||
static malloc_mutex_t tsd_nominal_tsds_lock;
|
||||
|
||||
/* How many slow-path-enabling features are turned on. */
|
||||
static atomic_u32_t tsd_global_slow_count = ATOMIC_INIT(0);
|
||||
|
||||
static bool
|
||||
tsd_in_nominal_list(tsd_t *tsd) {
|
||||
tsd_t *tsd_list;
|
||||
bool found = false;
|
||||
/*
|
||||
* We don't know that tsd is nominal; it might not be safe to get data
|
||||
* out of it here.
|
||||
*/
|
||||
malloc_mutex_lock(TSDN_NULL, &tsd_nominal_tsds_lock);
|
||||
ql_foreach(tsd_list, &tsd_nominal_tsds, TSD_MANGLE(tcache).tsd_link) {
|
||||
if (tsd == tsd_list) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
malloc_mutex_unlock(TSDN_NULL, &tsd_nominal_tsds_lock);
|
||||
return found;
|
||||
}
|
||||
|
||||
static void
|
||||
tsd_add_nominal(tsd_t *tsd) {
|
||||
assert(!tsd_in_nominal_list(tsd));
|
||||
assert(tsd_state_get(tsd) <= tsd_state_nominal_max);
|
||||
ql_elm_new(tsd, TSD_MANGLE(tcache).tsd_link);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
|
||||
ql_tail_insert(&tsd_nominal_tsds, tsd, TSD_MANGLE(tcache).tsd_link);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
tsd_remove_nominal(tsd_t *tsd) {
|
||||
assert(tsd_in_nominal_list(tsd));
|
||||
assert(tsd_state_get(tsd) <= tsd_state_nominal_max);
|
||||
malloc_mutex_lock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
|
||||
ql_remove(&tsd_nominal_tsds, tsd, TSD_MANGLE(tcache).tsd_link);
|
||||
malloc_mutex_unlock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
tsd_force_recompute(tsdn_t *tsdn) {
|
||||
/*
|
||||
* The stores to tsd->state here need to synchronize with the exchange
|
||||
* in tsd_slow_update.
|
||||
*/
|
||||
atomic_fence(ATOMIC_RELEASE);
|
||||
malloc_mutex_lock(tsdn, &tsd_nominal_tsds_lock);
|
||||
tsd_t *remote_tsd;
|
||||
ql_foreach(remote_tsd, &tsd_nominal_tsds, TSD_MANGLE(tcache).tsd_link) {
|
||||
assert(tsd_atomic_load(&remote_tsd->state, ATOMIC_RELAXED)
|
||||
<= tsd_state_nominal_max);
|
||||
tsd_atomic_store(&remote_tsd->state, tsd_state_nominal_recompute,
|
||||
ATOMIC_RELAXED);
|
||||
}
|
||||
malloc_mutex_unlock(tsdn, &tsd_nominal_tsds_lock);
|
||||
}
|
||||
|
||||
void
|
||||
tsd_global_slow_inc(tsdn_t *tsdn) {
|
||||
atomic_fetch_add_u32(&tsd_global_slow_count, 1, ATOMIC_RELAXED);
|
||||
/*
|
||||
* We unconditionally force a recompute, even if the global slow count
|
||||
* was already positive. If we didn't, then it would be possible for us
|
||||
* to return to the user, have the user synchronize externally with some
|
||||
* other thread, and then have that other thread not have picked up the
|
||||
* update yet (since the original incrementing thread might still be
|
||||
* making its way through the tsd list).
|
||||
*/
|
||||
tsd_force_recompute(tsdn);
|
||||
}
|
||||
|
||||
void tsd_global_slow_dec(tsdn_t *tsdn) {
|
||||
atomic_fetch_sub_u32(&tsd_global_slow_count, 1, ATOMIC_RELAXED);
|
||||
/* See the note in ..._inc(). */
|
||||
tsd_force_recompute(tsdn);
|
||||
}
|
||||
|
||||
static bool
|
||||
tsd_local_slow(tsd_t *tsd) {
|
||||
return !tsd_tcache_enabled_get(tsd)
|
||||
|| tsd_reentrancy_level_get(tsd) > 0;
|
||||
}
|
||||
|
||||
bool
|
||||
tsd_global_slow() {
|
||||
return atomic_load_u32(&tsd_global_slow_count, ATOMIC_RELAXED) > 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static uint8_t
|
||||
tsd_state_compute(tsd_t *tsd) {
|
||||
if (!tsd_nominal(tsd)) {
|
||||
return tsd_state_get(tsd);
|
||||
}
|
||||
/* We're in *a* nominal state; but which one? */
|
||||
if (malloc_slow || tsd_local_slow(tsd) || tsd_global_slow()) {
|
||||
return tsd_state_nominal_slow;
|
||||
} else {
|
||||
return tsd_state_nominal;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tsd_slow_update(tsd_t *tsd) {
|
||||
if (tsd_nominal(tsd)) {
|
||||
if (malloc_slow || !tsd_tcache_enabled_get(tsd) ||
|
||||
tsd_reentrancy_level_get(tsd) > 0) {
|
||||
tsd->state = tsd_state_nominal_slow;
|
||||
uint8_t old_state;
|
||||
do {
|
||||
uint8_t new_state = tsd_state_compute(tsd);
|
||||
old_state = tsd_atomic_exchange(&tsd->state, new_state,
|
||||
ATOMIC_ACQUIRE);
|
||||
} while (old_state == tsd_state_nominal_recompute);
|
||||
}
|
||||
|
||||
void
|
||||
tsd_state_set(tsd_t *tsd, uint8_t new_state) {
|
||||
/* Only the tsd module can change the state *to* recompute. */
|
||||
assert(new_state != tsd_state_nominal_recompute);
|
||||
uint8_t old_state = tsd_atomic_load(&tsd->state, ATOMIC_RELAXED);
|
||||
if (old_state > tsd_state_nominal_max) {
|
||||
/*
|
||||
* Not currently in the nominal list, but it might need to be
|
||||
* inserted there.
|
||||
*/
|
||||
assert(!tsd_in_nominal_list(tsd));
|
||||
tsd_atomic_store(&tsd->state, new_state, ATOMIC_RELAXED);
|
||||
if (new_state <= tsd_state_nominal_max) {
|
||||
tsd_add_nominal(tsd);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* We're currently nominal. If the new state is non-nominal,
|
||||
* great; we take ourselves off the list and just enter the new
|
||||
* state.
|
||||
*/
|
||||
assert(tsd_in_nominal_list(tsd));
|
||||
if (new_state > tsd_state_nominal_max) {
|
||||
tsd_remove_nominal(tsd);
|
||||
tsd_atomic_store(&tsd->state, new_state,
|
||||
ATOMIC_RELAXED);
|
||||
} else {
|
||||
tsd->state = tsd_state_nominal;
|
||||
/*
|
||||
* This is the tricky case. We're transitioning from
|
||||
* one nominal state to another. The caller can't know
|
||||
* about any races that are occuring at the same time,
|
||||
* so we always have to recompute no matter what.
|
||||
*/
|
||||
tsd_slow_update(tsd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,6 +239,7 @@ tsd_data_init(tsd_t *tsd) {
|
||||
static void
|
||||
assert_tsd_data_cleanup_done(tsd_t *tsd) {
|
||||
assert(!tsd_nominal(tsd));
|
||||
assert(!tsd_in_nominal_list(tsd));
|
||||
assert(*tsd_arenap_get_unsafe(tsd) == NULL);
|
||||
assert(*tsd_iarenap_get_unsafe(tsd) == NULL);
|
||||
assert(*tsd_arenas_tdata_bypassp_get_unsafe(tsd) == true);
|
||||
@ -97,8 +250,8 @@ assert_tsd_data_cleanup_done(tsd_t *tsd) {
|
||||
|
||||
static bool
|
||||
tsd_data_init_nocleanup(tsd_t *tsd) {
|
||||
assert(tsd->state == tsd_state_reincarnated ||
|
||||
tsd->state == tsd_state_minimal_initialized);
|
||||
assert(tsd_state_get(tsd) == tsd_state_reincarnated ||
|
||||
tsd_state_get(tsd) == tsd_state_minimal_initialized);
|
||||
/*
|
||||
* During reincarnation, there is no guarantee that the cleanup function
|
||||
* will be called (deallocation may happen after all tsd destructors).
|
||||
@ -117,27 +270,33 @@ tsd_t *
|
||||
tsd_fetch_slow(tsd_t *tsd, bool minimal) {
|
||||
assert(!tsd_fast(tsd));
|
||||
|
||||
if (tsd->state == tsd_state_nominal_slow) {
|
||||
/* On slow path but no work needed. */
|
||||
assert(malloc_slow || !tsd_tcache_enabled_get(tsd) ||
|
||||
tsd_reentrancy_level_get(tsd) > 0 ||
|
||||
*tsd_arenas_tdata_bypassp_get(tsd));
|
||||
} else if (tsd->state == tsd_state_uninitialized) {
|
||||
if (tsd_state_get(tsd) == tsd_state_nominal_slow) {
|
||||
/*
|
||||
* On slow path but no work needed. Note that we can't
|
||||
* necessarily *assert* that we're slow, because we might be
|
||||
* slow because of an asynchronous modification to global state,
|
||||
* which might be asynchronously modified *back*.
|
||||
*/
|
||||
} else if (tsd_state_get(tsd) == tsd_state_nominal_recompute) {
|
||||
tsd_slow_update(tsd);
|
||||
} else if (tsd_state_get(tsd) == tsd_state_uninitialized) {
|
||||
if (!minimal) {
|
||||
tsd->state = tsd_state_nominal;
|
||||
tsd_slow_update(tsd);
|
||||
/* Trigger cleanup handler registration. */
|
||||
tsd_set(tsd);
|
||||
tsd_data_init(tsd);
|
||||
if (tsd_booted) {
|
||||
tsd_state_set(tsd, tsd_state_nominal);
|
||||
tsd_slow_update(tsd);
|
||||
/* Trigger cleanup handler registration. */
|
||||
tsd_set(tsd);
|
||||
tsd_data_init(tsd);
|
||||
}
|
||||
} else {
|
||||
tsd->state = tsd_state_minimal_initialized;
|
||||
tsd_state_set(tsd, tsd_state_minimal_initialized);
|
||||
tsd_set(tsd);
|
||||
tsd_data_init_nocleanup(tsd);
|
||||
}
|
||||
} else if (tsd->state == tsd_state_minimal_initialized) {
|
||||
} else if (tsd_state_get(tsd) == tsd_state_minimal_initialized) {
|
||||
if (!minimal) {
|
||||
/* Switch to fully initialized. */
|
||||
tsd->state = tsd_state_nominal;
|
||||
tsd_state_set(tsd, tsd_state_nominal);
|
||||
assert(*tsd_reentrancy_levelp_get(tsd) >= 1);
|
||||
(*tsd_reentrancy_levelp_get(tsd))--;
|
||||
tsd_slow_update(tsd);
|
||||
@ -145,12 +304,12 @@ tsd_fetch_slow(tsd_t *tsd, bool minimal) {
|
||||
} else {
|
||||
assert_tsd_data_cleanup_done(tsd);
|
||||
}
|
||||
} else if (tsd->state == tsd_state_purgatory) {
|
||||
tsd->state = tsd_state_reincarnated;
|
||||
} else if (tsd_state_get(tsd) == tsd_state_purgatory) {
|
||||
tsd_state_set(tsd, tsd_state_reincarnated);
|
||||
tsd_set(tsd);
|
||||
tsd_data_init_nocleanup(tsd);
|
||||
} else {
|
||||
assert(tsd->state == tsd_state_reincarnated);
|
||||
assert(tsd_state_get(tsd) == tsd_state_reincarnated);
|
||||
}
|
||||
|
||||
return tsd;
|
||||
@ -214,7 +373,7 @@ void
|
||||
tsd_cleanup(void *arg) {
|
||||
tsd_t *tsd = (tsd_t *)arg;
|
||||
|
||||
switch (tsd->state) {
|
||||
switch (tsd_state_get(tsd)) {
|
||||
case tsd_state_uninitialized:
|
||||
/* Do nothing. */
|
||||
break;
|
||||
@ -232,7 +391,7 @@ tsd_cleanup(void *arg) {
|
||||
case tsd_state_nominal:
|
||||
case tsd_state_nominal_slow:
|
||||
tsd_do_data_cleanup(tsd);
|
||||
tsd->state = tsd_state_purgatory;
|
||||
tsd_state_set(tsd, tsd_state_purgatory);
|
||||
tsd_set(tsd);
|
||||
break;
|
||||
case tsd_state_purgatory:
|
||||
@ -260,6 +419,10 @@ malloc_tsd_boot0(void) {
|
||||
tsd_t *tsd;
|
||||
|
||||
ncleanups = 0;
|
||||
if (malloc_mutex_init(&tsd_nominal_tsds_lock, "tsd_nominal_tsds_lock",
|
||||
WITNESS_RANK_OMIT, malloc_mutex_rank_exclusive)) {
|
||||
return NULL;
|
||||
}
|
||||
if (tsd_boot0()) {
|
||||
return NULL;
|
||||
}
|
||||
@ -310,7 +473,7 @@ _tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
||||
# pragma comment(linker, "/INCLUDE:_tls_callback")
|
||||
# else
|
||||
# pragma comment(linker, "/INCLUDE:_tls_used")
|
||||
# pragma comment(linker, "/INCLUDE:tls_callback")
|
||||
# pragma comment(linker, "/INCLUDE:" STRINGIFY(tls_callback) )
|
||||
# endif
|
||||
# pragma section(".CRT$XLY",long,read)
|
||||
#endif
|
||||
@ -349,3 +512,23 @@ tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block) {
|
||||
malloc_mutex_unlock(TSDN_NULL, &head->lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
tsd_prefork(tsd_t *tsd) {
|
||||
malloc_mutex_prefork(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
|
||||
}
|
||||
|
||||
void
|
||||
tsd_postfork_parent(tsd_t *tsd) {
|
||||
malloc_mutex_postfork_parent(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
|
||||
}
|
||||
|
||||
void
|
||||
tsd_postfork_child(tsd_t *tsd) {
|
||||
malloc_mutex_postfork_child(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
|
||||
ql_new(&tsd_nominal_tsds);
|
||||
|
||||
if (tsd_state_get(tsd) <= tsd_state_nominal_max) {
|
||||
tsd_add_nominal(tsd);
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,10 @@
|
||||
.PATH: ${LIBC_SRCTOP}/stdlib/jemalloc
|
||||
|
||||
JEMALLOCSRCS:= jemalloc.c arena.c background_thread.c base.c bin.c bitmap.c \
|
||||
ckh.c ctl.c div.c extent.c extent_dss.c extent_mmap.c hash.c hooks.c \
|
||||
ckh.c ctl.c div.c extent.c extent_dss.c extent_mmap.c hash.c hook.c \
|
||||
large.c log.c malloc_io.c mutex.c mutex_pool.c nstime.c pages.c \
|
||||
prng.c prof.c rtree.c stats.c sz.c tcache.c ticker.c tsd.c witness.c
|
||||
prng.c prof.c rtree.c safety_check.c sc.c stats.c sz.c tcache.c \
|
||||
test_hooks.c ticker.c tsd.c witness.c
|
||||
|
||||
SYM_MAPS+=${LIBC_SRCTOP}/stdlib/jemalloc/Symbol.map
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user