Commit | Line | Data |
---|---|---|
7efdcf32 S |
1 | #include "rsync.h" |
2 | ||
3 | #define POOL_DEF_EXTENT (32 * 1024) | |
4 | ||
fb01d1fb | 5 | #define POOL_QALIGN_P2 (1<<16) /* power-of-2 qalign */ |
51ce67d5 | 6 | |
7efdcf32 S |
7 | struct alloc_pool |
8 | { | |
9 | size_t size; /* extent size */ | |
10 | size_t quantum; /* allocation quantum */ | |
676e6041 | 11 | struct pool_extent *extents; /* top extent is "live" */ |
e3d27df4 | 12 | void (*bomb)(); /* function to call if |
7efdcf32 S |
13 | * malloc fails */ |
14 | int flags; | |
15 | ||
16 | /* statistical data */ | |
17 | unsigned long e_created; /* extents created */ | |
51ce67d5 | 18 | unsigned long e_freed; /* extents destroyed */ |
707415d4 WD |
19 | int64 n_allocated; /* calls to alloc */ |
20 | int64 n_freed; /* calls to free */ | |
21 | int64 b_allocated; /* cum. bytes allocated */ | |
22 | int64 b_freed; /* cum. bytes freed */ | |
7efdcf32 S |
23 | }; |
24 | ||
25 | struct pool_extent | |
26 | { | |
0566dc54 | 27 | struct pool_extent *next; |
7efdcf32 S |
28 | void *start; /* starting address */ |
29 | size_t free; /* free bytecount */ | |
51ce67d5 | 30 | size_t bound; /* trapped free bytes */ |
7efdcf32 S |
31 | }; |
32 | ||
4d4df3cd | 33 | struct align_test { |
51ce67d5 WD |
34 | uchar foo; |
35 | union { | |
36 | int64 i; | |
37 | void *p; | |
38 | } bar; | |
4d4df3cd WD |
39 | }; |
40 | ||
41 | #define MINALIGN offsetof(struct align_test, bar) | |
7efdcf32 | 42 | |
71b291d7 WD |
43 | /* Temporarily cast a void* var into a char* var when adding an offset (to |
44 | * keep some compilers from complaining about the pointer arithmetic). */ | |
45 | #define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) ) | |
46 | ||
7efdcf32 | 47 | alloc_pool_t |
4a19c3b2 | 48 | pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags) |
7efdcf32 | 49 | { |
51ce67d5 | 50 | struct alloc_pool *pool; |
7efdcf32 | 51 | |
51ce67d5 WD |
52 | if (!(pool = new0(struct alloc_pool))) |
53 | return NULL; | |
54 | ||
55 | if ((MINALIGN & (MINALIGN - 1)) != 0) { | |
56 | if (bomb) | |
57 | (*bomb)("Compiler error: MINALIGN is not a power of 2\n"); | |
58 | return NULL; | |
59 | } | |
60 | ||
61 | if (!size) | |
62 | size = POOL_DEF_EXTENT; | |
63 | if (!quantum) | |
64 | quantum = MINALIGN; | |
7efdcf32 | 65 | |
a0f70237 | 66 | if (flags & POOL_INTERN) { |
51ce67d5 WD |
67 | if (size <= sizeof (struct pool_extent)) |
68 | size = quantum; | |
69 | else | |
70 | size -= sizeof (struct pool_extent); | |
71 | flags |= POOL_PREPEND; | |
7efdcf32 | 72 | } |
51ce67d5 WD |
73 | |
74 | if (quantum <= 1) | |
fb01d1fb WD |
75 | flags = (flags | POOL_NO_QALIGN) & ~POOL_QALIGN_P2; |
76 | else if (!(flags & POOL_NO_QALIGN)) { | |
51ce67d5 WD |
77 | if (size % quantum) |
78 | size += quantum - size % quantum; | |
79 | /* If quantum is a power of 2, we'll avoid using modulus. */ | |
80 | if (!(quantum & (quantum - 1))) | |
81 | flags |= POOL_QALIGN_P2; | |
82 | } | |
83 | ||
84 | pool->size = size; | |
85 | pool->quantum = quantum; | |
7efdcf32 S |
86 | pool->bomb = bomb; |
87 | pool->flags = flags; | |
88 | ||
89 | return pool; | |
90 | } | |
91 | ||
92 | void | |
93 | pool_destroy(alloc_pool_t p) | |
94 | { | |
95 | struct alloc_pool *pool = (struct alloc_pool *) p; | |
51ce67d5 | 96 | struct pool_extent *cur, *next; |
7efdcf32 S |
97 | |
98 | if (!pool) | |
99 | return; | |
100 | ||
676e6041 | 101 | for (cur = pool->extents; cur; cur = next) { |
7efdcf32 | 102 | next = cur->next; |
51ce67d5 | 103 | if (pool->flags & POOL_PREPEND) |
0566dc54 | 104 | free(PTR_ADD(cur->start, -sizeof (struct pool_extent))); |
51ce67d5 WD |
105 | else { |
106 | free(cur->start); | |
7efdcf32 | 107 | free(cur); |
51ce67d5 | 108 | } |
7efdcf32 | 109 | } |
51ce67d5 | 110 | |
7efdcf32 S |
111 | free(pool); |
112 | } | |
113 | ||
be20dc34 | 114 | void * |
3fac8ca8 | 115 | pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg) |
7efdcf32 S |
116 | { |
117 | struct alloc_pool *pool = (struct alloc_pool *) p; | |
118 | if (!pool) | |
15f85b1f | 119 | return NULL; |
7efdcf32 S |
120 | |
121 | if (!len) | |
122 | len = pool->quantum; | |
51ce67d5 WD |
123 | else if (pool->flags & POOL_QALIGN_P2) { |
124 | if (len & (pool->quantum - 1)) | |
125 | len += pool->quantum - (len & (pool->quantum - 1)); | |
fb01d1fb | 126 | } else if (!(pool->flags & POOL_NO_QALIGN)) { |
51ce67d5 WD |
127 | if (len % pool->quantum) |
128 | len += pool->quantum - len % pool->quantum; | |
129 | } | |
7efdcf32 S |
130 | |
131 | if (len > pool->size) | |
3fac8ca8 | 132 | goto bomb_out; |
7efdcf32 | 133 | |
676e6041 | 134 | if (!pool->extents || len > pool->extents->free) { |
51ce67d5 WD |
135 | void *start; |
136 | size_t asize; | |
e3d27df4 | 137 | struct pool_extent *ext; |
7efdcf32 | 138 | |
7efdcf32 | 139 | asize = pool->size; |
51ce67d5 | 140 | if (pool->flags & POOL_PREPEND) |
7efdcf32 S |
141 | asize += sizeof (struct pool_extent); |
142 | ||
e3d27df4 | 143 | if (!(start = new_array(char, asize))) |
3fac8ca8 | 144 | goto bomb_out; |
7efdcf32 S |
145 | |
146 | if (pool->flags & POOL_CLEAR) | |
51ce67d5 | 147 | memset(start, 0, asize); |
7efdcf32 | 148 | |
51ce67d5 WD |
149 | if (pool->flags & POOL_PREPEND) { |
150 | ext = start; | |
0566dc54 | 151 | start = PTR_ADD(start, sizeof (struct pool_extent)); |
51ce67d5 | 152 | } else if (!(ext = new(struct pool_extent))) |
3fac8ca8 | 153 | goto bomb_out; |
e3d27df4 | 154 | ext->start = start; |
51ce67d5 WD |
155 | ext->free = pool->size; |
156 | ext->bound = 0; | |
676e6041 WD |
157 | ext->next = pool->extents; |
158 | pool->extents = ext; | |
7efdcf32 S |
159 | |
160 | pool->e_created++; | |
161 | } | |
162 | ||
163 | pool->n_allocated++; | |
164 | pool->b_allocated += len; | |
165 | ||
676e6041 | 166 | pool->extents->free -= len; |
7efdcf32 | 167 | |
676e6041 | 168 | return PTR_ADD(pool->extents->start, pool->extents->free); |
7efdcf32 | 169 | |
3fac8ca8 | 170 | bomb_out: |
7efdcf32 | 171 | if (pool->bomb) |
3fac8ca8 | 172 | (*pool->bomb)(bomb_msg); |
7efdcf32 S |
173 | return NULL; |
174 | } | |
175 | ||
e3d27df4 WD |
176 | /* This function allows you to declare memory in the pool that you are done |
177 | * using. If you free all the memory in a pool's extent, that extent will | |
178 | * be freed. */ | |
7efdcf32 S |
179 | void |
180 | pool_free(alloc_pool_t p, size_t len, void *addr) | |
181 | { | |
e3d27df4 WD |
182 | struct alloc_pool *pool = (struct alloc_pool *)p; |
183 | struct pool_extent *cur, *prev; | |
7efdcf32 S |
184 | |
185 | if (!pool) | |
186 | return; | |
187 | ||
fb01d1fb WD |
188 | if (!addr) { |
189 | /* A NULL addr starts a fresh extent for new allocations. */ | |
190 | if ((cur = pool->extents) != NULL && cur->free != pool->size) { | |
191 | cur->bound += cur->free; | |
192 | cur->free = 0; | |
193 | } | |
194 | return; | |
195 | } | |
196 | ||
7efdcf32 S |
197 | if (!len) |
198 | len = pool->quantum; | |
51ce67d5 WD |
199 | else if (pool->flags & POOL_QALIGN_P2) { |
200 | if (len & (pool->quantum - 1)) | |
201 | len += pool->quantum - (len & (pool->quantum - 1)); | |
fb01d1fb | 202 | } else if (!(pool->flags & POOL_NO_QALIGN)) { |
51ce67d5 WD |
203 | if (len % pool->quantum) |
204 | len += pool->quantum - len % pool->quantum; | |
205 | } | |
7efdcf32 | 206 | |
7efdcf32 S |
207 | pool->n_freed++; |
208 | pool->b_freed += len; | |
209 | ||
676e6041 WD |
210 | for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) { |
211 | if (addr >= cur->start | |
212 | && addr < PTR_ADD(cur->start, pool->size)) | |
213 | break; | |
214 | } | |
215 | if (!cur) | |
216 | return; | |
217 | ||
218 | if (!prev) { | |
219 | /* The "live" extent is kept ready for more allocations. */ | |
220 | if (cur->free + cur->bound + len >= pool->size) { | |
e3d27df4 WD |
221 | if (pool->flags & POOL_CLEAR) { |
222 | memset(PTR_ADD(cur->start, cur->free), 0, | |
223 | pool->size - cur->free); | |
224 | } | |
7efdcf32 S |
225 | cur->free = pool->size; |
226 | cur->bound = 0; | |
676e6041 WD |
227 | } else if (addr == PTR_ADD(cur->start, cur->free)) { |
228 | if (pool->flags & POOL_CLEAR) | |
229 | memset(addr, 0, len); | |
230 | cur->free += len; | |
231 | } else | |
232 | cur->bound += len; | |
233 | } else { | |
234 | cur->bound += len; | |
235 | ||
236 | if (cur->free + cur->bound >= pool->size) { | |
237 | prev->next = cur->next; | |
51ce67d5 | 238 | if (pool->flags & POOL_PREPEND) |
0566dc54 | 239 | free(PTR_ADD(cur->start, -sizeof (struct pool_extent))); |
51ce67d5 WD |
240 | else { |
241 | free(cur->start); | |
676e6041 | 242 | free(cur); |
51ce67d5 | 243 | } |
676e6041 WD |
244 | pool->e_freed++; |
245 | } else if (prev != pool->extents) { | |
246 | /* Move the extent to be the first non-live extent. */ | |
247 | prev->next = cur->next; | |
248 | cur->next = pool->extents->next; | |
249 | pool->extents->next = cur; | |
7efdcf32 | 250 | } |
7efdcf32 | 251 | } |
676e6041 WD |
252 | } |
253 | ||
254 | /* This allows you to declare that the given address marks the edge of some | |
255 | * pool memory that is no longer needed. Any extents that hold only data | |
256 | * older than the boundary address are freed. NOTE: You MUST NOT USE BOTH | |
257 | * pool_free() and pool_free_old() on the same pool!! */ | |
258 | void | |
259 | pool_free_old(alloc_pool_t p, void *addr) | |
260 | { | |
261 | struct alloc_pool *pool = (struct alloc_pool *)p; | |
262 | struct pool_extent *cur, *prev, *next; | |
263 | ||
8b498b9f | 264 | if (!pool || !addr) |
676e6041 WD |
265 | return; |
266 | ||
267 | for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) { | |
7efdcf32 | 268 | if (addr >= cur->start |
04575bca | 269 | && addr < PTR_ADD(cur->start, pool->size)) |
7efdcf32 S |
270 | break; |
271 | } | |
272 | if (!cur) | |
273 | return; | |
274 | ||
676e6041 WD |
275 | if (addr == PTR_ADD(cur->start, cur->free)) { |
276 | if (prev) { | |
277 | prev->next = NULL; | |
278 | next = cur; | |
279 | } else { | |
676e6041 WD |
280 | /* The most recent live extent can just be reset. */ |
281 | if (pool->flags & POOL_CLEAR) | |
282 | memset(addr, 0, pool->size - cur->free); | |
283 | cur->free = pool->size; | |
284 | cur->bound = 0; | |
676e6041 | 285 | next = cur->next; |
65a22a5f | 286 | cur->next = NULL; |
676e6041 WD |
287 | } |
288 | } else { | |
289 | next = cur->next; | |
290 | cur->next = NULL; | |
291 | } | |
7efdcf32 | 292 | |
676e6041 WD |
293 | while ((cur = next) != NULL) { |
294 | next = cur->next; | |
51ce67d5 | 295 | if (pool->flags & POOL_PREPEND) |
0566dc54 | 296 | free(PTR_ADD(cur->start, -sizeof (struct pool_extent))); |
51ce67d5 WD |
297 | else { |
298 | free(cur->start); | |
7efdcf32 | 299 | free(cur); |
51ce67d5 | 300 | } |
7efdcf32 S |
301 | pool->e_freed++; |
302 | } | |
7efdcf32 S |
303 | } |
304 | ||
676e6041 WD |
305 | /* If the current extent doesn't have "len" free space in it, mark it as full |
306 | * so that the next alloc will start a new extent. If len is (size_t)-1, this | |
307 | * bump will always occur. The function returns a boundary address that can | |
308 | * be used with pool_free_old(), or a NULL if no memory is allocated. */ | |
309 | void * | |
310 | pool_boundary(alloc_pool_t p, size_t len) | |
311 | { | |
312 | struct alloc_pool *pool = (struct alloc_pool *)p; | |
313 | struct pool_extent *cur; | |
314 | ||
315 | if (!pool || !pool->extents) | |
316 | return NULL; | |
317 | ||
318 | cur = pool->extents; | |
319 | ||
320 | if (cur->free < len) { | |
321 | cur->bound += cur->free; | |
322 | cur->free = 0; | |
323 | } | |
324 | ||
325 | return PTR_ADD(cur->start, cur->free); | |
326 | } | |
327 | ||
7efdcf32 | 328 | #define FDPRINT(label, value) \ |
8ea17b50 WD |
329 | snprintf(buf, sizeof buf, label, value), \ |
330 | write(fd, buf, strlen(buf)) | |
7efdcf32 S |
331 | |
332 | #define FDEXTSTAT(ext) \ | |
8ea17b50 | 333 | snprintf(buf, sizeof buf, " %12ld %5ld\n", \ |
7efdcf32 S |
334 | (long) ext->free, \ |
335 | (long) ext->bound), \ | |
336 | write(fd, buf, strlen(buf)) | |
337 | ||
338 | void | |
339 | pool_stats(alloc_pool_t p, int fd, int summarize) | |
340 | { | |
341 | struct alloc_pool *pool = (struct alloc_pool *) p; | |
51ce67d5 | 342 | struct pool_extent *cur; |
7efdcf32 S |
343 | char buf[BUFSIZ]; |
344 | ||
345 | if (!pool) | |
346 | return; | |
347 | ||
348 | FDPRINT(" Extent size: %12ld\n", (long) pool->size); | |
349 | FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum); | |
350 | FDPRINT(" Extents created: %12ld\n", pool->e_created); | |
351 | FDPRINT(" Extents freed: %12ld\n", pool->e_freed); | |
352 | FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated); | |
353 | FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed); | |
e3d27df4 WD |
354 | FDPRINT(" Bytes allocated: %12.0f\n", (double) pool->b_allocated); |
355 | FDPRINT(" Bytes freed: %12.0f\n", (double) pool->b_freed); | |
7efdcf32 S |
356 | |
357 | if (summarize) | |
358 | return; | |
359 | ||
676e6041 | 360 | if (!pool->extents) |
7efdcf32 S |
361 | return; |
362 | ||
363 | write(fd, "\n", 1); | |
364 | ||
676e6041 | 365 | for (cur = pool->extents; cur; cur = cur->next) |
7efdcf32 | 366 | FDEXTSTAT(cur); |
7efdcf32 | 367 | } |