Commit | Line | Data |
---|---|---|
7efdcf32 S |
1 | #include "rsync.h" |
2 | ||
3 | #define POOL_DEF_EXTENT (32 * 1024) | |
4 | ||
51ce67d5 WD |
5 | #define POOL_QALIGN_P2 (1<<16) |
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 | { | |
27 | void *start; /* starting address */ | |
28 | size_t free; /* free bytecount */ | |
51ce67d5 | 29 | size_t bound; /* trapped free bytes */ |
7efdcf32 S |
30 | struct pool_extent *next; |
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) | |
75 | flags &= ~(POOL_QALIGN | POOL_QALIGN_P2); | |
76 | else if (flags & POOL_QALIGN) { | |
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 WD |
103 | if (pool->flags & POOL_PREPEND) |
104 | free(cur->start - sizeof (struct pool_extent)); | |
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)); | |
126 | } else if (pool->flags & POOL_QALIGN) { | |
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; | |
151 | start += sizeof (struct pool_extent); | |
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 | ||
188 | if (!len) | |
189 | len = pool->quantum; | |
51ce67d5 WD |
190 | else if (pool->flags & POOL_QALIGN_P2) { |
191 | if (len & (pool->quantum - 1)) | |
192 | len += pool->quantum - (len & (pool->quantum - 1)); | |
193 | } else if (pool->flags & POOL_QALIGN) { | |
194 | if (len % pool->quantum) | |
195 | len += pool->quantum - len % pool->quantum; | |
196 | } | |
7efdcf32 | 197 | |
7efdcf32 S |
198 | pool->n_freed++; |
199 | pool->b_freed += len; | |
200 | ||
676e6041 WD |
201 | for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) { |
202 | if (addr >= cur->start | |
203 | && addr < PTR_ADD(cur->start, pool->size)) | |
204 | break; | |
205 | } | |
206 | if (!cur) | |
207 | return; | |
208 | ||
209 | if (!prev) { | |
210 | /* The "live" extent is kept ready for more allocations. */ | |
211 | if (cur->free + cur->bound + len >= pool->size) { | |
e3d27df4 WD |
212 | if (pool->flags & POOL_CLEAR) { |
213 | memset(PTR_ADD(cur->start, cur->free), 0, | |
214 | pool->size - cur->free); | |
215 | } | |
7efdcf32 S |
216 | cur->free = pool->size; |
217 | cur->bound = 0; | |
676e6041 WD |
218 | } else if (addr == PTR_ADD(cur->start, cur->free)) { |
219 | if (pool->flags & POOL_CLEAR) | |
220 | memset(addr, 0, len); | |
221 | cur->free += len; | |
222 | } else | |
223 | cur->bound += len; | |
224 | } else { | |
225 | cur->bound += len; | |
226 | ||
227 | if (cur->free + cur->bound >= pool->size) { | |
228 | prev->next = cur->next; | |
51ce67d5 WD |
229 | if (pool->flags & POOL_PREPEND) |
230 | free(cur->start - sizeof (struct pool_extent)); | |
231 | else { | |
232 | free(cur->start); | |
676e6041 | 233 | free(cur); |
51ce67d5 | 234 | } |
676e6041 WD |
235 | pool->e_freed++; |
236 | } else if (prev != pool->extents) { | |
237 | /* Move the extent to be the first non-live extent. */ | |
238 | prev->next = cur->next; | |
239 | cur->next = pool->extents->next; | |
240 | pool->extents->next = cur; | |
7efdcf32 | 241 | } |
7efdcf32 | 242 | } |
676e6041 WD |
243 | } |
244 | ||
245 | /* This allows you to declare that the given address marks the edge of some | |
246 | * pool memory that is no longer needed. Any extents that hold only data | |
247 | * older than the boundary address are freed. NOTE: You MUST NOT USE BOTH | |
248 | * pool_free() and pool_free_old() on the same pool!! */ | |
249 | void | |
250 | pool_free_old(alloc_pool_t p, void *addr) | |
251 | { | |
252 | struct alloc_pool *pool = (struct alloc_pool *)p; | |
253 | struct pool_extent *cur, *prev, *next; | |
254 | ||
8b498b9f | 255 | if (!pool || !addr) |
676e6041 WD |
256 | return; |
257 | ||
258 | for (prev = NULL, cur = pool->extents; cur; prev = cur, cur = cur->next) { | |
7efdcf32 | 259 | if (addr >= cur->start |
04575bca | 260 | && addr < PTR_ADD(cur->start, pool->size)) |
7efdcf32 S |
261 | break; |
262 | } | |
263 | if (!cur) | |
264 | return; | |
265 | ||
676e6041 WD |
266 | if (addr == PTR_ADD(cur->start, cur->free)) { |
267 | if (prev) { | |
268 | prev->next = NULL; | |
269 | next = cur; | |
270 | } else { | |
676e6041 WD |
271 | /* The most recent live extent can just be reset. */ |
272 | if (pool->flags & POOL_CLEAR) | |
273 | memset(addr, 0, pool->size - cur->free); | |
274 | cur->free = pool->size; | |
275 | cur->bound = 0; | |
676e6041 | 276 | next = cur->next; |
65a22a5f | 277 | cur->next = NULL; |
676e6041 WD |
278 | } |
279 | } else { | |
280 | next = cur->next; | |
281 | cur->next = NULL; | |
282 | } | |
7efdcf32 | 283 | |
676e6041 WD |
284 | while ((cur = next) != NULL) { |
285 | next = cur->next; | |
51ce67d5 WD |
286 | if (pool->flags & POOL_PREPEND) |
287 | free(cur->start - sizeof (struct pool_extent)); | |
288 | else { | |
289 | free(cur->start); | |
7efdcf32 | 290 | free(cur); |
51ce67d5 | 291 | } |
7efdcf32 S |
292 | pool->e_freed++; |
293 | } | |
7efdcf32 S |
294 | } |
295 | ||
676e6041 WD |
296 | /* If the current extent doesn't have "len" free space in it, mark it as full |
297 | * so that the next alloc will start a new extent. If len is (size_t)-1, this | |
298 | * bump will always occur. The function returns a boundary address that can | |
299 | * be used with pool_free_old(), or a NULL if no memory is allocated. */ | |
300 | void * | |
301 | pool_boundary(alloc_pool_t p, size_t len) | |
302 | { | |
303 | struct alloc_pool *pool = (struct alloc_pool *)p; | |
304 | struct pool_extent *cur; | |
305 | ||
306 | if (!pool || !pool->extents) | |
307 | return NULL; | |
308 | ||
309 | cur = pool->extents; | |
310 | ||
311 | if (cur->free < len) { | |
312 | cur->bound += cur->free; | |
313 | cur->free = 0; | |
314 | } | |
315 | ||
316 | return PTR_ADD(cur->start, cur->free); | |
317 | } | |
318 | ||
7efdcf32 | 319 | #define FDPRINT(label, value) \ |
8ea17b50 WD |
320 | snprintf(buf, sizeof buf, label, value), \ |
321 | write(fd, buf, strlen(buf)) | |
7efdcf32 S |
322 | |
323 | #define FDEXTSTAT(ext) \ | |
8ea17b50 | 324 | snprintf(buf, sizeof buf, " %12ld %5ld\n", \ |
7efdcf32 S |
325 | (long) ext->free, \ |
326 | (long) ext->bound), \ | |
327 | write(fd, buf, strlen(buf)) | |
328 | ||
329 | void | |
330 | pool_stats(alloc_pool_t p, int fd, int summarize) | |
331 | { | |
332 | struct alloc_pool *pool = (struct alloc_pool *) p; | |
51ce67d5 | 333 | struct pool_extent *cur; |
7efdcf32 S |
334 | char buf[BUFSIZ]; |
335 | ||
336 | if (!pool) | |
337 | return; | |
338 | ||
339 | FDPRINT(" Extent size: %12ld\n", (long) pool->size); | |
340 | FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum); | |
341 | FDPRINT(" Extents created: %12ld\n", pool->e_created); | |
342 | FDPRINT(" Extents freed: %12ld\n", pool->e_freed); | |
343 | FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated); | |
344 | FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed); | |
e3d27df4 WD |
345 | FDPRINT(" Bytes allocated: %12.0f\n", (double) pool->b_allocated); |
346 | FDPRINT(" Bytes freed: %12.0f\n", (double) pool->b_freed); | |
7efdcf32 S |
347 | |
348 | if (summarize) | |
349 | return; | |
350 | ||
676e6041 | 351 | if (!pool->extents) |
7efdcf32 S |
352 | return; |
353 | ||
354 | write(fd, "\n", 1); | |
355 | ||
676e6041 | 356 | for (cur = pool->extents; cur; cur = cur->next) |
7efdcf32 | 357 | FDEXTSTAT(cur); |
7efdcf32 | 358 | } |