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