1 | /* |
2 | Copyright (c) 2000, 2010, Oracle and/or its affiliates |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
16 | |
17 | /* Routines to handle mallocing of results which will be freed the same time */ |
18 | |
19 | #include <my_global.h> |
20 | #include <my_sys.h> |
21 | #include <m_string.h> |
22 | #undef EXTRA_DEBUG |
23 | #define |
24 | |
25 | /* data packed in MEM_ROOT -> min_malloc */ |
26 | |
27 | #define MALLOC_FLAG(A) ((A & 1) ? MY_THREAD_SPECIFIC : 0) |
28 | |
29 | #define TRASH_MEM(X) TRASH_FREE(((char*)(X) + ((X)->size-(X)->left)), (X)->left) |
30 | |
31 | /* |
32 | Initialize memory root |
33 | |
34 | SYNOPSIS |
35 | init_alloc_root() |
36 | mem_root - memory root to initialize |
37 | name - name of memroot (for debugging) |
38 | block_size - size of chunks (blocks) used for memory allocation |
39 | (It is external size of chunk i.e. it should include |
40 | memory required for internal structures, thus it |
41 | should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE) |
42 | pre_alloc_size - if non-0, then size of block that should be |
43 | pre-allocated during memory root initialization. |
44 | my_flags MY_THREAD_SPECIFIC flag for my_malloc |
45 | |
46 | DESCRIPTION |
47 | This function prepares memory root for further use, sets initial size of |
48 | chunk for memory allocation and pre-allocates first block if specified. |
49 | Although error can happen during execution of this function if |
50 | pre_alloc_size is non-0 it won't be reported. Instead it will be |
51 | reported as error in first alloc_root() on this memory root. |
52 | |
53 | We don't want to change the structure size for MEM_ROOT. |
54 | Because of this, we store in MY_THREAD_SPECIFIC as bit 1 in block_size |
55 | */ |
56 | |
57 | void init_alloc_root(MEM_ROOT *mem_root, const char *name, size_t block_size, |
58 | size_t pre_alloc_size __attribute__((unused)), |
59 | myf my_flags) |
60 | { |
61 | DBUG_ENTER("init_alloc_root" ); |
62 | DBUG_PRINT("enter" ,("root: %p name: %s prealloc: %zu" , mem_root, |
63 | name, pre_alloc_size)); |
64 | |
65 | mem_root->free= mem_root->used= mem_root->pre_alloc= 0; |
66 | mem_root->min_malloc= 32; |
67 | mem_root->block_size= (block_size - ALLOC_ROOT_MIN_BLOCK_SIZE) & ~1; |
68 | if (MY_TEST(my_flags & MY_THREAD_SPECIFIC)) |
69 | mem_root->block_size|= 1; |
70 | |
71 | mem_root->error_handler= 0; |
72 | mem_root->block_num= 4; /* We shift this with >>2 */ |
73 | mem_root->first_block_usage= 0; |
74 | mem_root->total_alloc= 0; |
75 | mem_root->name= name; |
76 | |
77 | #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) |
78 | if (pre_alloc_size) |
79 | { |
80 | if ((mem_root->free= mem_root->pre_alloc= |
81 | (USED_MEM*) my_malloc(pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM)), |
82 | MYF(my_flags)))) |
83 | { |
84 | mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)); |
85 | mem_root->total_alloc= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)); |
86 | mem_root->free->left= pre_alloc_size; |
87 | mem_root->free->next= 0; |
88 | TRASH_MEM(mem_root->free); |
89 | } |
90 | } |
91 | #endif |
92 | DBUG_VOID_RETURN; |
93 | } |
94 | |
95 | /* |
96 | SYNOPSIS |
97 | reset_root_defaults() |
98 | mem_root memory root to change defaults of |
99 | block_size new value of block size. Must be greater or equal |
100 | than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about |
101 | 68 bytes and depends on platform and compilation flags) |
102 | pre_alloc_size new size of preallocated block. If not zero, |
103 | must be equal to or greater than block size, |
104 | otherwise means 'no prealloc'. |
105 | DESCRIPTION |
106 | Function aligns and assigns new value to block size; then it tries to |
107 | reuse one of existing blocks as prealloc block, or malloc new one of |
108 | requested size. If no blocks can be reused, all unused blocks are freed |
109 | before allocation. |
110 | */ |
111 | |
112 | void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, |
113 | size_t pre_alloc_size __attribute__((unused))) |
114 | { |
115 | DBUG_ENTER("reset_root_defaults" ); |
116 | DBUG_ASSERT(alloc_root_inited(mem_root)); |
117 | |
118 | mem_root->block_size= (((block_size - ALLOC_ROOT_MIN_BLOCK_SIZE) & ~1) | |
119 | (mem_root->block_size & 1)); |
120 | #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) |
121 | if (pre_alloc_size) |
122 | { |
123 | size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM)); |
124 | if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size) |
125 | { |
126 | USED_MEM *mem, **prev= &mem_root->free; |
127 | /* |
128 | Free unused blocks, so that consequent calls |
129 | to reset_root_defaults won't eat away memory. |
130 | */ |
131 | while (*prev) |
132 | { |
133 | mem= *prev; |
134 | if (mem->size == size) |
135 | { |
136 | /* We found a suitable block, no need to do anything else */ |
137 | mem_root->pre_alloc= mem; |
138 | DBUG_VOID_RETURN; |
139 | } |
140 | if (mem->left + ALIGN_SIZE(sizeof(USED_MEM)) == mem->size) |
141 | { |
142 | /* remove block from the list and free it */ |
143 | *prev= mem->next; |
144 | mem_root->total_alloc-= mem->size; |
145 | my_free(mem); |
146 | } |
147 | else |
148 | prev= &mem->next; |
149 | } |
150 | /* Allocate new prealloc block and add it to the end of free list */ |
151 | if ((mem= (USED_MEM *) my_malloc(size, |
152 | MYF(MALLOC_FLAG(mem_root-> |
153 | block_size))))) |
154 | { |
155 | mem->size= size; |
156 | mem_root->total_alloc+= size; |
157 | mem->left= pre_alloc_size; |
158 | mem->next= *prev; |
159 | *prev= mem_root->pre_alloc= mem; |
160 | TRASH_MEM(mem); |
161 | } |
162 | else |
163 | { |
164 | mem_root->pre_alloc= 0; |
165 | } |
166 | } |
167 | } |
168 | else |
169 | #endif |
170 | mem_root->pre_alloc= 0; |
171 | |
172 | DBUG_VOID_RETURN; |
173 | } |
174 | |
175 | |
176 | void *alloc_root(MEM_ROOT *mem_root, size_t length) |
177 | { |
178 | #if defined(HAVE_valgrind) && defined(EXTRA_DEBUG) |
179 | reg1 USED_MEM *next; |
180 | DBUG_ENTER("alloc_root" ); |
181 | DBUG_PRINT("enter" ,("root: %p name: %s" , mem_root, mem_root->name)); |
182 | |
183 | DBUG_ASSERT(alloc_root_inited(mem_root)); |
184 | |
185 | DBUG_EXECUTE_IF("simulate_out_of_memory" , |
186 | { |
187 | if (mem_root->error_handler) |
188 | (*mem_root->error_handler)(); |
189 | DBUG_SET("-d,simulate_out_of_memory" ); |
190 | DBUG_RETURN((void*) 0); /* purecov: inspected */ |
191 | }); |
192 | |
193 | length+=ALIGN_SIZE(sizeof(USED_MEM)); |
194 | if (!(next = (USED_MEM*) my_malloc(length, |
195 | MYF(MY_WME | ME_FATALERROR | |
196 | MALLOC_FLAG(mem_root->block_size))))) |
197 | { |
198 | if (mem_root->error_handler) |
199 | (*mem_root->error_handler)(); |
200 | DBUG_RETURN((uchar*) 0); /* purecov: inspected */ |
201 | } |
202 | next->next= mem_root->used; |
203 | next->left= 0; |
204 | next->size= length; |
205 | mem_root->used= next; |
206 | mem_root->total_alloc+= length; |
207 | DBUG_PRINT("exit" ,("ptr: %p" , (((char*) next)+ |
208 | ALIGN_SIZE(sizeof(USED_MEM))))); |
209 | DBUG_RETURN((uchar*) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM)))); |
210 | #else |
211 | size_t get_size, block_size; |
212 | uchar* point; |
213 | reg1 USED_MEM *next= 0; |
214 | reg2 USED_MEM **prev; |
215 | DBUG_ENTER("alloc_root" ); |
216 | DBUG_PRINT("enter" ,("root: %p name: %s" , mem_root, mem_root->name)); |
217 | DBUG_ASSERT(alloc_root_inited(mem_root)); |
218 | |
219 | DBUG_EXECUTE_IF("simulate_out_of_memory" , |
220 | { |
221 | /* Avoid reusing an already allocated block */ |
222 | if (mem_root->error_handler) |
223 | (*mem_root->error_handler)(); |
224 | DBUG_SET("-d,simulate_out_of_memory" ); |
225 | DBUG_RETURN((void*) 0); /* purecov: inspected */ |
226 | }); |
227 | length= ALIGN_SIZE(length); |
228 | if ((*(prev= &mem_root->free)) != NULL) |
229 | { |
230 | if ((*prev)->left < length && |
231 | mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP && |
232 | (*prev)->left < ALLOC_MAX_BLOCK_TO_DROP) |
233 | { |
234 | next= *prev; |
235 | *prev= next->next; /* Remove block from list */ |
236 | next->next= mem_root->used; |
237 | mem_root->used= next; |
238 | mem_root->first_block_usage= 0; |
239 | } |
240 | for (next= *prev ; next && next->left < length ; next= next->next) |
241 | prev= &next->next; |
242 | } |
243 | if (! next) |
244 | { /* Time to alloc new block */ |
245 | block_size= (mem_root->block_size & ~1) * (mem_root->block_num >> 2); |
246 | get_size= length+ALIGN_SIZE(sizeof(USED_MEM)); |
247 | get_size= MY_MAX(get_size, block_size); |
248 | |
249 | if (!(next = (USED_MEM*) my_malloc(get_size, |
250 | MYF(MY_WME | ME_FATALERROR | |
251 | MALLOC_FLAG(mem_root-> |
252 | block_size))))) |
253 | { |
254 | if (mem_root->error_handler) |
255 | (*mem_root->error_handler)(); |
256 | DBUG_RETURN((void*) 0); /* purecov: inspected */ |
257 | } |
258 | mem_root->block_num++; |
259 | mem_root->total_alloc+= get_size; |
260 | next->next= *prev; |
261 | next->size= get_size; |
262 | next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM)); |
263 | *prev=next; |
264 | TRASH_MEM(next); |
265 | } |
266 | |
267 | point= (uchar*) ((char*) next+ (next->size-next->left)); |
268 | /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/ |
269 | if ((next->left-= length) < mem_root->min_malloc) |
270 | { /* Full block */ |
271 | *prev= next->next; /* Remove block from list */ |
272 | next->next= mem_root->used; |
273 | mem_root->used= next; |
274 | mem_root->first_block_usage= 0; |
275 | } |
276 | TRASH_ALLOC(point, length); |
277 | DBUG_PRINT("exit" ,("ptr: %p" , point)); |
278 | DBUG_RETURN((void*) point); |
279 | #endif |
280 | } |
281 | |
282 | |
283 | /* |
284 | Allocate many pointers at the same time. |
285 | |
286 | DESCRIPTION |
287 | ptr1, ptr2, etc all point into big allocated memory area. |
288 | |
289 | SYNOPSIS |
290 | multi_alloc_root() |
291 | root Memory root |
292 | ptr1, length1 Multiple arguments terminated by a NULL pointer |
293 | ptr2, length2 ... |
294 | ... |
295 | NULL |
296 | |
297 | RETURN VALUE |
298 | A pointer to the beginning of the allocated memory block |
299 | in case of success or NULL if out of memory. |
300 | */ |
301 | |
302 | void *multi_alloc_root(MEM_ROOT *root, ...) |
303 | { |
304 | va_list args; |
305 | char **ptr, *start, *res; |
306 | size_t tot_length, length; |
307 | DBUG_ENTER("multi_alloc_root" ); |
308 | /* |
309 | We don't need to do DBUG_PRINT here as it will be done when alloc_root |
310 | is called |
311 | */ |
312 | |
313 | va_start(args, root); |
314 | tot_length= 0; |
315 | while ((ptr= va_arg(args, char **))) |
316 | { |
317 | length= va_arg(args, uint); |
318 | tot_length+= ALIGN_SIZE(length); |
319 | } |
320 | va_end(args); |
321 | |
322 | if (!(start= (char*) alloc_root(root, tot_length))) |
323 | DBUG_RETURN(0); /* purecov: inspected */ |
324 | |
325 | va_start(args, root); |
326 | res= start; |
327 | while ((ptr= va_arg(args, char **))) |
328 | { |
329 | *ptr= res; |
330 | length= va_arg(args, uint); |
331 | res+= ALIGN_SIZE(length); |
332 | } |
333 | va_end(args); |
334 | DBUG_RETURN((void*) start); |
335 | } |
336 | |
337 | |
338 | #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) |
339 | /** Mark all data in blocks free for reusage */ |
340 | |
341 | static inline void mark_blocks_free(MEM_ROOT* root) |
342 | { |
343 | reg1 USED_MEM *next; |
344 | reg2 USED_MEM **last; |
345 | |
346 | /* iterate through (partially) free blocks, mark them free */ |
347 | last= &root->free; |
348 | for (next= root->free; next; next= *(last= &next->next)) |
349 | { |
350 | next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM)); |
351 | TRASH_MEM(next); |
352 | } |
353 | |
354 | /* Combine the free and the used list */ |
355 | *last= next=root->used; |
356 | |
357 | /* now go through the used blocks and mark them free */ |
358 | for (; next; next= next->next) |
359 | { |
360 | next->left= next->size - ALIGN_SIZE(sizeof(USED_MEM)); |
361 | TRASH_MEM(next); |
362 | } |
363 | |
364 | /* Now everything is set; Indicate that nothing is used anymore */ |
365 | root->used= 0; |
366 | root->first_block_usage= 0; |
367 | root->block_num= 4; |
368 | } |
369 | #endif |
370 | |
371 | |
372 | /* |
373 | Deallocate everything used by alloc_root or just move |
374 | used blocks to free list if called with MY_USED_TO_FREE |
375 | |
376 | SYNOPSIS |
377 | free_root() |
378 | root Memory root |
379 | MyFlags Flags for what should be freed: |
380 | |
381 | MY_MARK_BLOCKS_FREED Don't free blocks, just mark them free |
382 | MY_KEEP_PREALLOC If this is not set, then free also the |
383 | preallocated block |
384 | |
385 | NOTES |
386 | One can call this function either with root block initialised with |
387 | init_alloc_root() or with a bzero()-ed block. |
388 | It's also safe to call this multiple times with the same mem_root. |
389 | */ |
390 | |
391 | void free_root(MEM_ROOT *root, myf MyFlags) |
392 | { |
393 | reg1 USED_MEM *next,*old; |
394 | DBUG_ENTER("free_root" ); |
395 | DBUG_PRINT("enter" ,("root: %p name: %s flags: %u" , root, root->name, |
396 | (uint) MyFlags)); |
397 | |
398 | #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) |
399 | /* |
400 | There is no point in using mark_blocks_free when using valgrind as |
401 | it will not reclaim any memory |
402 | */ |
403 | if (MyFlags & MY_MARK_BLOCKS_FREE) |
404 | { |
405 | mark_blocks_free(root); |
406 | DBUG_VOID_RETURN; |
407 | } |
408 | #endif |
409 | if (!(MyFlags & MY_KEEP_PREALLOC)) |
410 | root->pre_alloc=0; |
411 | |
412 | for (next=root->used; next ;) |
413 | { |
414 | old=next; next= next->next ; |
415 | if (old != root->pre_alloc) |
416 | { |
417 | root->total_alloc-= old->size; |
418 | my_free(old); |
419 | } |
420 | } |
421 | for (next=root->free ; next ;) |
422 | { |
423 | old=next; next= next->next; |
424 | if (old != root->pre_alloc) |
425 | { |
426 | root->total_alloc-= old->size; |
427 | my_free(old); |
428 | } |
429 | } |
430 | root->used=root->free=0; |
431 | if (root->pre_alloc) |
432 | { |
433 | root->free=root->pre_alloc; |
434 | root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(USED_MEM)); |
435 | TRASH_MEM(root->pre_alloc); |
436 | root->free->next=0; |
437 | } |
438 | root->block_num= 4; |
439 | root->first_block_usage= 0; |
440 | DBUG_VOID_RETURN; |
441 | } |
442 | |
443 | /* |
444 | Find block that contains an object and set the pre_alloc to it |
445 | */ |
446 | |
447 | void set_prealloc_root(MEM_ROOT *root, char *ptr) |
448 | { |
449 | USED_MEM *next; |
450 | for (next=root->used; next ; next=next->next) |
451 | { |
452 | if ((char*) next <= ptr && (char*) next + next->size > ptr) |
453 | { |
454 | root->pre_alloc=next; |
455 | return; |
456 | } |
457 | } |
458 | for (next=root->free ; next ; next=next->next) |
459 | { |
460 | if ((char*) next <= ptr && (char*) next + next->size > ptr) |
461 | { |
462 | root->pre_alloc=next; |
463 | return; |
464 | } |
465 | } |
466 | } |
467 | |
468 | |
469 | char *strdup_root(MEM_ROOT *root, const char *str) |
470 | { |
471 | return strmake_root(root, str, strlen(str)); |
472 | } |
473 | |
474 | |
475 | char *strmake_root(MEM_ROOT *root, const char *str, size_t len) |
476 | { |
477 | char *pos; |
478 | if ((pos=alloc_root(root,len+1))) |
479 | { |
480 | memcpy(pos,str,len); |
481 | pos[len]=0; |
482 | } |
483 | return pos; |
484 | } |
485 | |
486 | |
487 | void *memdup_root(MEM_ROOT *root, const void *str, size_t len) |
488 | { |
489 | char *pos; |
490 | if ((pos=alloc_root(root,len))) |
491 | memcpy(pos,str,len); |
492 | return pos; |
493 | } |
494 | |