Another attempt at measuring the minimum alignment for a system.
[rsync/rsync.git] / lib / pool_alloc.c
1 #include "rsync.h"
2
3 #define POOL_DEF_EXTENT (32 * 1024)
4
5 struct alloc_pool
6 {
7         size_t                  size;           /* extent size          */
8         size_t                  quantum;        /* allocation quantum   */
9         struct pool_extent      *live;          /* current extent for
10                                                  * allocations          */
11         struct pool_extent      *free;          /* unfreed extent list  */
12         void                    (*bomb)();
13                                                 /* function to call if
14                                                  * malloc fails         */
15         int                     flags;
16
17         /* statistical data */
18         unsigned long           e_created;      /* extents created      */
19         unsigned long           e_freed;        /* extents detroyed     */
20         uint64                  n_allocated;    /* calls to alloc       */
21         uint64                  n_freed;        /* calls to free        */
22         uint64                  b_allocated;    /* cum. bytes allocated */
23         uint64                  b_freed;        /* cum. bytes freed     */
24 };
25
26 struct pool_extent
27 {
28         void                    *start;         /* starting address     */
29         size_t                  free;           /* free bytecount       */
30         size_t                  bound;          /* bytes bound by padding,
31                                                  * overhead and freed   */
32         struct pool_extent      *next;
33 };
34
35 struct align_test {
36     void *foo;
37     uint64 bar;
38 };
39
40 #define MINALIGN        offsetof(struct align_test, bar)
41
42 alloc_pool_t
43 pool_create(size_t size, size_t quantum,
44     void (*bomb)(char *), int flags)
45 {
46         struct alloc_pool       *pool;
47
48         if (!(pool = (struct alloc_pool*) malloc(sizeof (struct alloc_pool))))
49                 return pool;
50         memset(pool, 0, sizeof (struct alloc_pool));
51
52         pool->size = size       /* round extent size to min alignment reqs */
53             ? (size + MINALIGN - 1) & ~(MINALIGN - 1)
54             : POOL_DEF_EXTENT;
55         if (pool->flags & POOL_INTERN)
56         {
57                 pool->size -= sizeof (struct pool_extent);
58                 flags |= POOL_APPEND;
59         }
60         pool->quantum = quantum ? quantum : MINALIGN;
61         pool->bomb = bomb;
62         pool->flags = flags;
63
64         return pool;
65 }
66
67 void
68 pool_destroy(alloc_pool_t p)
69 {
70         struct alloc_pool *pool = (struct alloc_pool *) p;
71         struct pool_extent      *cur, *next;
72
73         if (!pool)
74                 return;
75
76         if (pool->live)
77         {
78                 cur = pool->live;
79                 free(cur->start);
80                 if (!(pool->flags & POOL_APPEND))
81                         free(cur);
82         }
83         for (cur = pool->free; cur; cur = next)
84         {
85                 next = cur->next;
86                 free(cur->start);
87                 if (!(pool->flags & POOL_APPEND))
88                         free(cur);
89         }
90         free(pool);
91 }
92
93 void *
94 pool_alloc(alloc_pool_t p, size_t len, char *bomb)
95 {
96         struct alloc_pool *pool = (struct alloc_pool *) p;
97         if (!pool)
98                 return NULL;
99
100         if (!len)
101                 len = pool->quantum;
102         else if (pool->quantum > 1 && len % pool->quantum)
103                 len += pool->quantum - len % pool->quantum;
104
105         if (len > pool->size)
106                 goto bomb;
107
108         if (!pool->live || len > pool->live->free)
109         {
110                 void    *start;
111                 size_t  free;
112                 size_t  bound;
113                 size_t  sqew;
114                 size_t  asize;
115
116                 if (pool->live)
117                 {
118                         pool->live->next = pool->free;
119                         pool->free = pool->live;
120                 }
121
122                 free = pool->size;
123                 bound = 0;
124
125                 asize = pool->size;
126                 if (pool->flags & POOL_APPEND)
127                         asize += sizeof (struct pool_extent);
128
129                 if (!(start = (void *) malloc(asize)))
130                         goto bomb;
131
132                 if (pool->flags & POOL_CLEAR)
133                         memset(start, 0, pool->size);
134
135                 if (pool->flags & POOL_APPEND)
136                 {
137                         pool->live = start + free;
138                 }
139                 else if (!(pool->live = (struct pool_extent *) malloc(sizeof (struct pool_extent))))
140                 {
141                         goto bomb;
142                 }
143                 if (pool->flags & POOL_QALIGN && pool->quantum > 1
144                     && (sqew = (size_t)(start + free) % pool->quantum))
145                 {
146                         bound  += sqew;
147                         free -= sqew;
148                 }
149                 pool->live->start = start;
150                 pool->live->free = free;
151                 pool->live->bound = bound;
152                 pool->live->next = NULL;
153
154                 pool->e_created++;
155         }
156
157         pool->n_allocated++;
158         pool->b_allocated += len;
159
160         pool->live->free -= len;
161
162         return pool->live->start + pool->live->free;
163
164 bomb:
165         if (pool->bomb)
166                 (*pool->bomb)(bomb);
167         return NULL;
168 }
169
170 void
171 pool_free(alloc_pool_t p, size_t len, void *addr)
172 {
173         struct alloc_pool *pool = (struct alloc_pool *) p;
174         struct pool_extent      *cur;
175         struct pool_extent      *prev;
176
177         if (!pool)
178                 return;
179
180         if (!len)
181                 len = pool->quantum;
182         else if (pool->quantum > 1 && len % pool->quantum)
183                 len += pool->quantum - len % pool->quantum;
184
185         if (!addr && pool->live)
186         {
187                 pool->live->next = pool->free;
188                 pool->free = pool->live;
189                 pool->live = NULL;
190                 return;
191         }
192         pool->n_freed++;
193         pool->b_freed += len;
194
195         cur = pool->live;
196         if (cur
197             && addr >= cur->start
198             && addr < cur->start + pool->size)
199         {
200                 if (addr == cur->start + cur->free)
201                 {
202                         if (pool->flags & POOL_CLEAR)
203                                 memset(addr, 0, len);
204                         pool->b_freed += len;
205                 } else {
206                         cur->bound += len;
207                 }
208                 if (cur->free + cur->bound >= pool->size)
209                 {
210                         size_t sqew;
211
212                         cur->free = pool->size;
213                         cur->bound = 0;
214                         if (pool->flags & POOL_QALIGN && pool->quantum > 1
215                             && (sqew = (size_t)(cur->start + cur->free) % pool->quantum))
216                         {
217                                 cur->bound += sqew;
218                                 cur->free -= sqew;
219                         }
220                 }
221                 return;
222         }
223         for (prev = NULL, cur = pool->free; cur; prev = cur, cur = cur->next)
224         {
225                 if (addr >= cur->start
226                     && addr < cur->start + pool->size)
227                         break;
228         }
229         if (!cur)
230                 return;
231
232         if (prev)
233         {
234                 prev->next = cur->next;
235                 cur->next = pool->free;
236                 pool->free = cur;
237         }
238         cur->bound += len;
239
240         if (cur->free + cur->bound >= pool->size)
241         {
242                 pool->free = cur->next;
243
244                 free(cur->start);
245                 if (!(pool->flags & POOL_APPEND))
246                         free(cur);
247                 pool->e_freed++;
248         }
249         return;
250 }
251
252 #define FDPRINT(label, value) \
253         snprintf(buf, BUFSIZ, label, value), \
254         write(fd, buf, strlen(buf));
255
256 #define FDEXTSTAT(ext) \
257         snprintf(buf, BUFSIZ, "  %12ld  %5ld\n", \
258                 (long) ext->free, \
259                 (long) ext->bound), \
260         write(fd, buf, strlen(buf))
261
262 void
263 pool_stats(alloc_pool_t p, int fd, int summarize)
264 {
265         struct alloc_pool *pool = (struct alloc_pool *) p;
266         struct pool_extent      *cur;
267         char buf[BUFSIZ];
268
269         if (!pool)
270                 return;
271
272         FDPRINT("  Extent size:       %12ld\n", (long)  pool->size);
273         FDPRINT("  Alloc quantum:     %12ld\n", (long)  pool->quantum);
274         FDPRINT("  Extents created:   %12ld\n",         pool->e_created);
275         FDPRINT("  Extents freed:     %12ld\n",         pool->e_freed);
276         FDPRINT("  Alloc count:       %12.0f\n", (double) pool->n_allocated);
277         FDPRINT("  Free Count:        %12.0f\n", (double) pool->n_freed);
278         FDPRINT("  Alloc bytes:       %12.0f\n", (double) pool->b_allocated);
279         FDPRINT("  Free bytes:        %12.0f\n", (double) pool->b_freed);
280
281         if (summarize)
282                 return;
283
284         if (!pool->live && !pool->free)
285                 return;
286
287         write(fd, "\n", 1);
288
289         if (pool->live)
290         {
291                 FDEXTSTAT(pool->live);
292         }
293         strcpy(buf, "   FREE    BOUND\n");
294         write(fd, buf, strlen(buf));
295
296         for (cur = pool->free; cur; cur = cur->next)
297         {
298                 FDEXTSTAT(cur);
299         }
300 }