1 #include "jemalloc/internal/jemalloc_internal.h"
4 * quarantine pointers close to NULL are used to encode state information that
5 * is used for cleaning up during thread shutdown.
7 #define QUARANTINE_STATE_REINCARNATED ((quarantine_t *)(uintptr_t)1)
8 #define QUARANTINE_STATE_PURGATORY ((quarantine_t *)(uintptr_t)2)
9 #define QUARANTINE_STATE_MAX QUARANTINE_STATE_PURGATORY
11 /******************************************************************************/
14 typedef struct quarantine_obj_s quarantine_obj_t
;
15 typedef struct quarantine_s quarantine_t
;
17 struct quarantine_obj_s
{
26 #define LG_MAXOBJS_INIT 10
28 quarantine_obj_t objs
[1]; /* Dynamically sized ring buffer. */
31 static void quarantine_cleanup(void *arg
);
33 malloc_tsd_data(static, quarantine
, quarantine_t
*, NULL
)
34 malloc_tsd_funcs(JEMALLOC_INLINE
, quarantine
, quarantine_t
*, NULL
,
37 /******************************************************************************/
38 /* Function prototypes for non-inline static functions. */
40 static quarantine_t
*quarantine_init(size_t lg_maxobjs
);
41 static quarantine_t
*quarantine_grow(quarantine_t
*quarantine
);
42 static void quarantine_drain(quarantine_t
*quarantine
, size_t upper_bound
);
44 /******************************************************************************/
47 quarantine_init(size_t lg_maxobjs
)
49 quarantine_t
*quarantine
;
51 quarantine
= (quarantine_t
*)imalloc(offsetof(quarantine_t
, objs
) +
52 ((ZU(1) << lg_maxobjs
) * sizeof(quarantine_obj_t
)));
53 if (quarantine
== NULL
)
55 quarantine
->curbytes
= 0;
56 quarantine
->curobjs
= 0;
57 quarantine
->first
= 0;
58 quarantine
->lg_maxobjs
= lg_maxobjs
;
60 quarantine_tsd_set(&quarantine
);
66 quarantine_grow(quarantine_t
*quarantine
)
70 ret
= quarantine_init(quarantine
->lg_maxobjs
+ 1);
74 ret
->curbytes
= quarantine
->curbytes
;
75 ret
->curobjs
= quarantine
->curobjs
;
76 if (quarantine
->first
+ quarantine
->curobjs
<= (ZU(1) <<
77 quarantine
->lg_maxobjs
)) {
78 /* objs ring buffer data are contiguous. */
79 memcpy(ret
->objs
, &quarantine
->objs
[quarantine
->first
],
80 quarantine
->curobjs
* sizeof(quarantine_obj_t
));
82 /* objs ring buffer data wrap around. */
83 size_t ncopy_a
= (ZU(1) << quarantine
->lg_maxobjs
) -
85 size_t ncopy_b
= quarantine
->curobjs
- ncopy_a
;
87 memcpy(ret
->objs
, &quarantine
->objs
[quarantine
->first
], ncopy_a
88 * sizeof(quarantine_obj_t
));
89 memcpy(&ret
->objs
[ncopy_a
], quarantine
->objs
, ncopy_b
*
90 sizeof(quarantine_obj_t
));
97 quarantine_drain(quarantine_t
*quarantine
, size_t upper_bound
)
100 while (quarantine
->curbytes
> upper_bound
&& quarantine
->curobjs
> 0) {
101 quarantine_obj_t
*obj
= &quarantine
->objs
[quarantine
->first
];
102 assert(obj
->usize
== isalloc(obj
->ptr
, config_prof
));
104 quarantine
->curbytes
-= obj
->usize
;
105 quarantine
->curobjs
--;
106 quarantine
->first
= (quarantine
->first
+ 1) & ((ZU(1) <<
107 quarantine
->lg_maxobjs
) - 1);
112 quarantine(void *ptr
)
114 quarantine_t
*quarantine
;
115 size_t usize
= isalloc(ptr
, config_prof
);
117 cassert(config_fill
);
118 assert(opt_quarantine
);
120 quarantine
= *quarantine_tsd_get();
121 if ((uintptr_t)quarantine
<= (uintptr_t)QUARANTINE_STATE_MAX
) {
122 if (quarantine
== NULL
) {
123 if ((quarantine
= quarantine_init(LG_MAXOBJS_INIT
)) ==
129 if (quarantine
== QUARANTINE_STATE_PURGATORY
) {
131 * Make a note that quarantine() was called
132 * after quarantine_cleanup() was called.
134 quarantine
= QUARANTINE_STATE_REINCARNATED
;
135 quarantine_tsd_set(&quarantine
);
142 * Drain one or more objects if the quarantine size limit would be
143 * exceeded by appending ptr.
145 if (quarantine
->curbytes
+ usize
> opt_quarantine
) {
146 size_t upper_bound
= (opt_quarantine
>= usize
) ? opt_quarantine
148 quarantine_drain(quarantine
, upper_bound
);
150 /* Grow the quarantine ring buffer if it's full. */
151 if (quarantine
->curobjs
== (ZU(1) << quarantine
->lg_maxobjs
))
152 quarantine
= quarantine_grow(quarantine
);
153 /* quarantine_grow() must free a slot if it fails to grow. */
154 assert(quarantine
->curobjs
< (ZU(1) << quarantine
->lg_maxobjs
));
155 /* Append ptr if its size doesn't exceed the quarantine size. */
156 if (quarantine
->curbytes
+ usize
<= opt_quarantine
) {
157 size_t offset
= (quarantine
->first
+ quarantine
->curobjs
) &
158 ((ZU(1) << quarantine
->lg_maxobjs
) - 1);
159 quarantine_obj_t
*obj
= &quarantine
->objs
[offset
];
162 quarantine
->curbytes
+= usize
;
163 quarantine
->curobjs
++;
165 memset(ptr
, 0x5a, usize
);
167 assert(quarantine
->curbytes
== 0);
173 quarantine_cleanup(void *arg
)
175 quarantine_t
*quarantine
= *(quarantine_t
**)arg
;
177 if (quarantine
== QUARANTINE_STATE_REINCARNATED
) {
179 * Another destructor deallocated memory after this destructor
180 * was called. Reset quarantine to QUARANTINE_STATE_PURGATORY
181 * in order to receive another callback.
183 quarantine
= QUARANTINE_STATE_PURGATORY
;
184 quarantine_tsd_set(&quarantine
);
185 } else if (quarantine
== QUARANTINE_STATE_PURGATORY
) {
187 * The previous time this destructor was called, we set the key
188 * to QUARANTINE_STATE_PURGATORY so that other destructors
189 * wouldn't cause re-creation of the quarantine. This time, do
190 * nothing, so that the destructor will not be called again.
192 } else if (quarantine
!= NULL
) {
193 quarantine_drain(quarantine
, 0);
195 quarantine
= QUARANTINE_STATE_PURGATORY
;
196 quarantine_tsd_set(&quarantine
);
201 quarantine_boot(void)
204 cassert(config_fill
);
206 if (quarantine_tsd_boot())