|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifndef plarena_h___ |
|
7 #define plarena_h___ |
|
8 /* |
|
9 * Lifetime-based fast allocation, inspired by much prior art, including |
|
10 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" |
|
11 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). |
|
12 * |
|
13 * Also supports LIFO allocation (PL_ARENA_MARK/PL_ARENA_RELEASE). |
|
14 */ |
|
15 #include "prtypes.h" |
|
16 #include "plarenas.h" |
|
17 |
|
18 PR_BEGIN_EXTERN_C |
|
19 |
|
20 typedef struct PLArena PLArena; |
|
21 |
|
22 struct PLArena { |
|
23 PLArena *next; /* next arena for this lifetime */ |
|
24 PRUword base; /* aligned base address, follows this header */ |
|
25 PRUword limit; /* one beyond last byte in arena */ |
|
26 PRUword avail; /* points to next available byte */ |
|
27 }; |
|
28 |
|
29 #ifdef PL_ARENAMETER |
|
30 typedef struct PLArenaStats PLArenaStats; |
|
31 |
|
32 struct PLArenaStats { |
|
33 PLArenaStats *next; /* next in arenaStats list */ |
|
34 char *name; /* name for debugging */ |
|
35 PRUint32 narenas; /* number of arenas in pool */ |
|
36 PRUint32 nallocs; /* number of PL_ARENA_ALLOCATE() calls */ |
|
37 PRUint32 nreclaims; /* number of reclaims from freeArenas */ |
|
38 PRUint32 nmallocs; /* number of malloc() calls */ |
|
39 PRUint32 ndeallocs; /* number of lifetime deallocations */ |
|
40 PRUint32 ngrows; /* number of PL_ARENA_GROW() calls */ |
|
41 PRUint32 ninplace; /* number of in-place growths */ |
|
42 PRUint32 nreleases; /* number of PL_ARENA_RELEASE() calls */ |
|
43 PRUint32 nfastrels; /* number of "fast path" releases */ |
|
44 PRUint32 nbytes; /* total bytes allocated */ |
|
45 PRUint32 maxalloc; /* maximum allocation size in bytes */ |
|
46 PRFloat64 variance; /* size variance accumulator */ |
|
47 }; |
|
48 #endif |
|
49 |
|
50 struct PLArenaPool { |
|
51 PLArena first; /* first arena in pool list */ |
|
52 PLArena *current; /* arena from which to allocate space */ |
|
53 PRUint32 arenasize; /* net exact size of a new arena */ |
|
54 PRUword mask; /* alignment mask (power-of-2 - 1) */ |
|
55 #ifdef PL_ARENAMETER |
|
56 PLArenaStats stats; |
|
57 #endif |
|
58 }; |
|
59 |
|
60 /* |
|
61 * WARNING: The PL_MAKE_MEM_ macros are for internal use by NSPR. Do NOT use |
|
62 * them in your code. |
|
63 * |
|
64 * NOTE: Valgrind support to be added. |
|
65 * |
|
66 * The PL_MAKE_MEM_ macros are modeled after the MOZ_MAKE_MEM_ macros in |
|
67 * Mozilla's mfbt/MemoryChecking.h. Only AddressSanitizer is supported now. |
|
68 * |
|
69 * Provides a common interface to the ASan (AddressSanitizer) and Valgrind |
|
70 * functions used to mark memory in certain ways. In detail, the following |
|
71 * three macros are provided: |
|
72 * |
|
73 * PL_MAKE_MEM_NOACCESS - Mark memory as unsafe to access (e.g. freed) |
|
74 * PL_MAKE_MEM_UNDEFINED - Mark memory as accessible, with content undefined |
|
75 * PL_MAKE_MEM_DEFINED - Mark memory as accessible, with content defined |
|
76 * |
|
77 * With Valgrind in use, these directly map to the three respective Valgrind |
|
78 * macros. With ASan in use, the NOACCESS macro maps to poisoning the memory, |
|
79 * while the UNDEFINED/DEFINED macros unpoison memory. |
|
80 * |
|
81 * With no memory checker available, all macros expand to the empty statement. |
|
82 */ |
|
83 |
|
84 /* WARNING: PL_SANITIZE_ADDRESS is for internal use by this header. Do NOT |
|
85 * define or test this macro in your code. |
|
86 */ |
|
87 #if defined(__has_feature) |
|
88 #if __has_feature(address_sanitizer) |
|
89 #define PL_SANITIZE_ADDRESS 1 |
|
90 #endif |
|
91 #elif defined(__SANITIZE_ADDRESS__) |
|
92 #define PL_SANITIZE_ADDRESS 1 |
|
93 #endif |
|
94 |
|
95 #if defined(PL_SANITIZE_ADDRESS) |
|
96 |
|
97 /* These definitions are usually provided through the |
|
98 * sanitizer/asan_interface.h header installed by ASan. |
|
99 * See https://code.google.com/p/address-sanitizer/wiki/ManualPoisoning |
|
100 */ |
|
101 |
|
102 void __asan_poison_memory_region(void const volatile *addr, size_t size); |
|
103 void __asan_unpoison_memory_region(void const volatile *addr, size_t size); |
|
104 |
|
105 #define PL_MAKE_MEM_NOACCESS(addr, size) \ |
|
106 __asan_poison_memory_region((addr), (size)) |
|
107 |
|
108 #define PL_MAKE_MEM_UNDEFINED(addr, size) \ |
|
109 __asan_unpoison_memory_region((addr), (size)) |
|
110 |
|
111 #define PL_MAKE_MEM_DEFINED(addr, size) \ |
|
112 __asan_unpoison_memory_region((addr), (size)) |
|
113 |
|
114 #else |
|
115 |
|
116 #define PL_MAKE_MEM_NOACCESS(addr, size) |
|
117 #define PL_MAKE_MEM_UNDEFINED(addr, size) |
|
118 #define PL_MAKE_MEM_DEFINED(addr, size) |
|
119 |
|
120 #endif |
|
121 |
|
122 /* |
|
123 * If the including .c file uses only one power-of-2 alignment, it may define |
|
124 * PL_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions |
|
125 * per ALLOCATE and GROW. |
|
126 */ |
|
127 #ifdef PL_ARENA_CONST_ALIGN_MASK |
|
128 #define PL_ARENA_ALIGN(pool, n) (((PRUword)(n) + PL_ARENA_CONST_ALIGN_MASK) \ |
|
129 & ~PL_ARENA_CONST_ALIGN_MASK) |
|
130 |
|
131 #define PL_INIT_ARENA_POOL(pool, name, size) \ |
|
132 PL_InitArenaPool(pool, name, size, PL_ARENA_CONST_ALIGN_MASK + 1) |
|
133 #else |
|
134 #define PL_ARENA_ALIGN(pool, n) (((PRUword)(n) + (pool)->mask) & ~(pool)->mask) |
|
135 #endif |
|
136 |
|
137 #define PL_ARENA_ALLOCATE(p, pool, nb) \ |
|
138 PR_BEGIN_MACRO \ |
|
139 PLArena *_a = (pool)->current; \ |
|
140 PRUint32 _nb = PL_ARENA_ALIGN(pool, nb); \ |
|
141 PRUword _p = _a->avail; \ |
|
142 PRUword _q = _p + _nb; \ |
|
143 if (_q > _a->limit) { \ |
|
144 _p = (PRUword)PL_ArenaAllocate(pool, _nb); \ |
|
145 } else { \ |
|
146 _a->avail = _q; \ |
|
147 } \ |
|
148 p = (void *)_p; \ |
|
149 PL_MAKE_MEM_UNDEFINED(p, nb); \ |
|
150 PL_ArenaCountAllocation(pool, nb); \ |
|
151 PR_END_MACRO |
|
152 |
|
153 #define PL_ARENA_GROW(p, pool, size, incr) \ |
|
154 PR_BEGIN_MACRO \ |
|
155 PLArena *_a = (pool)->current; \ |
|
156 PRUint32 _incr = PL_ARENA_ALIGN(pool, incr); \ |
|
157 PRUword _p = _a->avail; \ |
|
158 PRUword _q = _p + _incr; \ |
|
159 if (_p == (PRUword)(p) + PL_ARENA_ALIGN(pool, size) && \ |
|
160 _q <= _a->limit) { \ |
|
161 PL_MAKE_MEM_UNDEFINED((unsigned char *)(p) + size, incr); \ |
|
162 _a->avail = _q; \ |
|
163 PL_ArenaCountInplaceGrowth(pool, size, incr); \ |
|
164 } else { \ |
|
165 p = PL_ArenaGrow(pool, p, size, incr); \ |
|
166 } \ |
|
167 PL_ArenaCountGrowth(pool, size, incr); \ |
|
168 PR_END_MACRO |
|
169 |
|
170 #define PL_ARENA_MARK(pool) ((void *) (pool)->current->avail) |
|
171 #define PR_UPTRDIFF(p,q) ((PRUword)(p) - (PRUword)(q)) |
|
172 |
|
173 #define PL_CLEAR_UNUSED_PATTERN(a, pattern) \ |
|
174 PR_BEGIN_MACRO \ |
|
175 PR_ASSERT((a)->avail <= (a)->limit); \ |
|
176 PL_MAKE_MEM_UNDEFINED((void*)(a)->avail, (a)->limit - (a)->avail); \ |
|
177 memset((void*)(a)->avail, (pattern), (a)->limit - (a)->avail); \ |
|
178 PR_END_MACRO |
|
179 #ifdef DEBUG |
|
180 #define PL_FREE_PATTERN 0xDA |
|
181 #define PL_CLEAR_UNUSED(a) PL_CLEAR_UNUSED_PATTERN((a), PL_FREE_PATTERN) |
|
182 #define PL_CLEAR_ARENA(a) \ |
|
183 PR_BEGIN_MACRO \ |
|
184 PL_MAKE_MEM_UNDEFINED((void*)(a), (a)->limit - (PRUword)(a)); \ |
|
185 memset((void*)(a), PL_FREE_PATTERN, (a)->limit - (PRUword)(a)); \ |
|
186 PR_END_MACRO |
|
187 #else |
|
188 #define PL_CLEAR_UNUSED(a) |
|
189 #define PL_CLEAR_ARENA(a) |
|
190 #endif |
|
191 |
|
192 #define PL_ARENA_RELEASE(pool, mark) \ |
|
193 PR_BEGIN_MACRO \ |
|
194 char *_m = (char *)(mark); \ |
|
195 PLArena *_a = (pool)->current; \ |
|
196 if (PR_UPTRDIFF(_m, _a->base) <= PR_UPTRDIFF(_a->avail, _a->base)) { \ |
|
197 _a->avail = (PRUword)PL_ARENA_ALIGN(pool, _m); \ |
|
198 PL_CLEAR_UNUSED(_a); \ |
|
199 PL_MAKE_MEM_NOACCESS((void*)_a->avail, _a->limit - _a->avail); \ |
|
200 PL_ArenaCountRetract(pool, _m); \ |
|
201 } else { \ |
|
202 PL_ArenaRelease(pool, _m); \ |
|
203 } \ |
|
204 PL_ArenaCountRelease(pool, _m); \ |
|
205 PR_END_MACRO |
|
206 |
|
207 #ifdef PL_ARENAMETER |
|
208 #define PL_COUNT_ARENA(pool,op) ((pool)->stats.narenas op) |
|
209 #else |
|
210 #define PL_COUNT_ARENA(pool,op) |
|
211 #endif |
|
212 |
|
213 #define PL_ARENA_DESTROY(pool, a, pnext) \ |
|
214 PR_BEGIN_MACRO \ |
|
215 PL_COUNT_ARENA(pool,--); \ |
|
216 if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ |
|
217 *(pnext) = (a)->next; \ |
|
218 PL_CLEAR_ARENA(a); \ |
|
219 free(a); \ |
|
220 (a) = 0; \ |
|
221 PR_END_MACRO |
|
222 |
|
223 #ifdef PL_ARENAMETER |
|
224 |
|
225 #include <stdio.h> |
|
226 |
|
227 PR_EXTERN(void) PL_ArenaCountAllocation(PLArenaPool *pool, PRUint32 nb); |
|
228 |
|
229 PR_EXTERN(void) PL_ArenaCountInplaceGrowth( |
|
230 PLArenaPool *pool, PRUint32 size, PRUint32 incr); |
|
231 |
|
232 PR_EXTERN(void) PL_ArenaCountGrowth( |
|
233 PLArenaPool *pool, PRUint32 size, PRUint32 incr); |
|
234 |
|
235 PR_EXTERN(void) PL_ArenaCountRelease(PLArenaPool *pool, char *mark); |
|
236 |
|
237 PR_EXTERN(void) PL_ArenaCountRetract(PLArenaPool *pool, char *mark); |
|
238 |
|
239 PR_EXTERN(void) PL_DumpArenaStats(FILE *fp); |
|
240 |
|
241 #else /* !PL_ARENAMETER */ |
|
242 |
|
243 #define PL_ArenaCountAllocation(ap, nb) /* nothing */ |
|
244 #define PL_ArenaCountInplaceGrowth(ap, size, incr) /* nothing */ |
|
245 #define PL_ArenaCountGrowth(ap, size, incr) /* nothing */ |
|
246 #define PL_ArenaCountRelease(ap, mark) /* nothing */ |
|
247 #define PL_ArenaCountRetract(ap, mark) /* nothing */ |
|
248 |
|
249 #endif /* !PL_ARENAMETER */ |
|
250 |
|
251 PR_END_EXTERN_C |
|
252 |
|
253 #endif /* plarena_h___ */ |