MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mem0pool.cc
Go to the documentation of this file.
1 /*****************************************************************************
2 
3 Copyright (c) 1997, 2011, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
16 
17 *****************************************************************************/
18 
19 /********************************************************************/
26 #include "mem0pool.h"
27 #ifdef UNIV_NONINL
28 #include "mem0pool.ic"
29 #endif
30 
31 #include "srv0srv.h"
32 #include "sync0sync.h"
33 #include "ut0mem.h"
34 #include "ut0lst.h"
35 #include "ut0byte.h"
36 #include "mem0mem.h"
37 #include "srv0start.h"
38 
39 /* We would like to use also the buffer frames to allocate memory. This
40 would be desirable, because then the memory consumption of the database
41 would be fixed, and we might even lock the buffer pool to the main memory.
42 The problem here is that the buffer management routines can themselves call
43 memory allocation, while the buffer pool mutex is reserved.
44 
45 The main components of the memory consumption are:
46 
47 1. buffer pool,
48 2. parsed and optimized SQL statements,
49 3. data dictionary cache,
50 4. log buffer,
51 5. locks for each transaction,
52 6. hash table for the adaptive index,
53 7. state and buffers for each SQL query currently being executed,
54 8. session for each user, and
55 9. stack for each OS thread.
56 
57 Items 1 and 2 are managed by an LRU algorithm. Items 5 and 6 can potentially
58 consume very much memory. Items 7 and 8 should consume quite little memory,
59 and the OS should take care of item 9, which too should consume little memory.
60 
61 A solution to the memory management:
62 
63 1. the buffer pool size is set separately;
64 2. log buffer size is set separately;
65 3. the common pool size for all the other entries, except 8, is set separately.
66 
67 Problems: we may waste memory if the common pool is set too big. Another
68 problem is the locks, which may take very much space in big transactions.
69 Then the shared pool size should be set very big. We can allow locks to take
70 space from the buffer pool, but the SQL optimizer is then unaware of the
71 usable size of the buffer pool. We could also combine the objects in the
72 common pool and the buffers in the buffer pool into a single LRU list and
73 manage it uniformly, but this approach does not take into account the parsing
74 and other costs unique to SQL statements.
75 
76 The locks for a transaction can be seen as a part of the state of the
77 transaction. Hence, they should be stored in the common pool. We still
78 have the problem of a very big update transaction, for example, which
79 will set very many x-locks on rows, and the locks will consume a lot
80 of memory, say, half of the buffer pool size.
81 
82 Another problem is what to do if we are not able to malloc a requested
83 block of memory from the common pool. Then we can request memory from
84 the operating system. If it does not help, a system error results.
85 
86 Because 5 and 6 may potentially consume very much memory, we let them grow
87 into the buffer pool. We may let the locks of a transaction take frames
88 from the buffer pool, when the corresponding memory heap block has grown to
89 the size of a buffer frame. Similarly for the hash node cells of the locks,
90 and for the adaptive index. Thus, for each individual transaction, its locks
91 can occupy at most about the size of the buffer frame of memory in the common
92 pool, and after that its locks will grow into the buffer pool. */
93 
95 #define MEM_AREA_FREE 1
96 
98 #define MEM_AREA_MIN_SIZE (2 * MEM_AREA_EXTRA_SIZE)
99 
100 
103 struct mem_pool_t{
104  byte* buf;
105  ulint size;
106  ulint reserved;
109  UT_LIST_BASE_NODE_T(mem_area_t)
110  free_list[64];
113 };
114 
116 UNIV_INTERN mem_pool_t* mem_comm_pool = NULL;
117 
118 #ifdef UNIV_PFS_MUTEX
119 /* Key to register mutex in mem_pool_t with performance schema */
120 UNIV_INTERN mysql_pfs_key_t mem_pool_mutex_key;
121 #endif /* UNIV_PFS_MUTEX */
122 
123 /* We use this counter to check that the mem pool mutex does not leak;
124 this is to track a strange assertion failure reported at
125 mysql@lists.mysql.com */
126 
127 UNIV_INTERN ulint mem_n_threads_inside = 0;
128 
129 /********************************************************************/
133 UNIV_INLINE
134 void
136 /*=================*/
137  mem_pool_t* pool)
138 {
140  mutex_enter(&(pool->mutex));
141  }
142 }
143 
144 /********************************************************************/
148 UNIV_INLINE
149 void
151 /*================*/
152  mem_pool_t* pool)
153 {
155  mutex_exit(&(pool->mutex));
156  }
157 }
158 
159 /********************************************************************/
162 UNIV_INLINE
163 ulint
165 /*==============*/
166  mem_area_t* area)
167 {
168  return(area->size_and_free & ~MEM_AREA_FREE);
169 }
170 
171 /********************************************************************/
173 UNIV_INLINE
174 void
176 /*==============*/
177  mem_area_t* area,
178  ulint size)
179 {
180  area->size_and_free = (area->size_and_free & MEM_AREA_FREE)
181  | size;
182 }
183 
184 /********************************************************************/
187 UNIV_INLINE
188 ibool
190 /*==============*/
191  mem_area_t* area)
192 {
193 #if TRUE != MEM_AREA_FREE
194 # error "TRUE != MEM_AREA_FREE"
195 #endif
196  return(area->size_and_free & MEM_AREA_FREE);
197 }
198 
199 /********************************************************************/
201 UNIV_INLINE
202 void
204 /*==============*/
205  mem_area_t* area,
206  ibool free)
207 {
208 #if TRUE != MEM_AREA_FREE
209 # error "TRUE != MEM_AREA_FREE"
210 #endif
211  area->size_and_free = (area->size_and_free & ~MEM_AREA_FREE)
212  | free;
213 }
214 
215 /********************************************************************/
218 UNIV_INTERN
219 mem_pool_t*
221 /*============*/
222  ulint size)
223 {
224  mem_pool_t* pool;
225  mem_area_t* area;
226  ulint i;
227  ulint used;
228 
229  pool = static_cast<mem_pool_t*>(ut_malloc(sizeof(mem_pool_t)));
230 
231  pool->buf = static_cast<byte*>(ut_malloc_low(size, TRUE));
232  pool->size = size;
233 
234  mutex_create(mem_pool_mutex_key, &pool->mutex, SYNC_MEM_POOL);
235 
236  /* Initialize the free lists */
237 
238  for (i = 0; i < 64; i++) {
239 
240  UT_LIST_INIT(pool->free_list[i]);
241  }
242 
243  used = 0;
244 
245  while (size - used >= MEM_AREA_MIN_SIZE) {
246 
247  i = ut_2_log(size - used);
248 
249  if (ut_2_exp(i) > size - used) {
250 
251  /* ut_2_log rounds upward */
252 
253  i--;
254  }
255 
256  area = (mem_area_t*)(pool->buf + used);
257 
258  mem_area_set_size(area, ut_2_exp(i));
259  mem_area_set_free(area, TRUE);
260  UNIV_MEM_FREE(MEM_AREA_EXTRA_SIZE + (byte*) area,
262 
263  UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
264 
265  used = used + ut_2_exp(i);
266  }
267 
268  ut_ad(size >= used);
269 
270  pool->reserved = 0;
271 
272  return(pool);
273 }
274 
275 /********************************************************************/
277 UNIV_INTERN
278 void
280 /*==========*/
281  mem_pool_t* pool)
282 {
283  ut_free(pool->buf);
284  ut_free(pool);
285 }
286 
287 /********************************************************************/
290 static
291 ibool
292 mem_pool_fill_free_list(
293 /*====================*/
294  ulint i,
295  mem_pool_t* pool)
296 {
297  mem_area_t* area;
298  mem_area_t* area2;
299  ibool ret;
300 
301  ut_ad(mutex_own(&(pool->mutex)));
302 
303  if (UNIV_UNLIKELY(i >= 63)) {
304  /* We come here when we have run out of space in the
305  memory pool: */
306 
307  return(FALSE);
308  }
309 
310  area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
311 
312  if (area == NULL) {
313  if (UT_LIST_GET_LEN(pool->free_list[i + 1]) > 0) {
314  ut_print_timestamp(stderr);
315 
316  fprintf(stderr,
317  " InnoDB: Error: mem pool free list %lu"
318  " length is %lu\n"
319  "InnoDB: though the list is empty!\n",
320  (ulong) i + 1,
321  (ulong)
322  UT_LIST_GET_LEN(pool->free_list[i + 1]));
323  }
324 
325  ret = mem_pool_fill_free_list(i + 1, pool);
326 
327  if (ret == FALSE) {
328 
329  return(FALSE);
330  }
331 
332  area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
333  }
334 
335  if (UNIV_UNLIKELY(UT_LIST_GET_LEN(pool->free_list[i + 1]) == 0)) {
337 
338  ut_error;
339  }
340 
341  UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area);
342 
343  area2 = (mem_area_t*)(((byte*) area) + ut_2_exp(i));
344  UNIV_MEM_ALLOC(area2, MEM_AREA_EXTRA_SIZE);
345 
346  mem_area_set_size(area2, ut_2_exp(i));
347  mem_area_set_free(area2, TRUE);
348 
349  UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area2);
350 
351  mem_area_set_size(area, ut_2_exp(i));
352 
353  UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
354 
355  return(TRUE);
356 }
357 
358 /********************************************************************/
362 UNIV_INTERN
363 void*
365 /*===========*/
366  ulint* psize,
371  mem_pool_t* pool)
372 {
373  mem_area_t* area;
374  ulint size;
375  ulint n;
376  ibool ret;
377 
378  /* If we are using os allocator just make a simple call
379  to malloc */
380  if (UNIV_LIKELY(srv_use_sys_malloc)) {
381  return(malloc(*psize));
382  }
383 
384  size = *psize;
386 
387  mutex_enter(&(pool->mutex));
388  mem_n_threads_inside++;
389 
390  ut_a(mem_n_threads_inside == 1);
391 
392  area = UT_LIST_GET_FIRST(pool->free_list[n]);
393 
394  if (area == NULL) {
395  ret = mem_pool_fill_free_list(n, pool);
396 
397  if (ret == FALSE) {
398  /* Out of memory in memory pool: we try to allocate
399  from the operating system with the regular malloc: */
400 
401  mem_n_threads_inside--;
402  mutex_exit(&(pool->mutex));
403 
404  return(ut_malloc(size));
405  }
406 
407  area = UT_LIST_GET_FIRST(pool->free_list[n]);
408  }
409 
410  if (!mem_area_get_free(area)) {
411  fprintf(stderr,
412  "InnoDB: Error: Removing element from mem pool"
413  " free list %lu though the\n"
414  "InnoDB: element is not marked free!\n",
415  (ulong) n);
416 
418 
419  /* Try to analyze a strange assertion failure reported at
420  mysql@lists.mysql.com where the free bit IS 1 in the
421  hex dump above */
422 
423  if (mem_area_get_free(area)) {
424  fprintf(stderr,
425  "InnoDB: Probably a race condition"
426  " because now the area is marked free!\n");
427  }
428 
429  ut_error;
430  }
431 
432  if (UT_LIST_GET_LEN(pool->free_list[n]) == 0) {
433  fprintf(stderr,
434  "InnoDB: Error: Removing element from mem pool"
435  " free list %lu\n"
436  "InnoDB: though the list length is 0!\n",
437  (ulong) n);
439 
440  ut_error;
441  }
442 
443  ut_ad(mem_area_get_size(area) == ut_2_exp(n));
444 
445  mem_area_set_free(area, FALSE);
446 
447  UT_LIST_REMOVE(free_list, pool->free_list[n], area);
448 
449  pool->reserved += mem_area_get_size(area);
450 
451  mem_n_threads_inside--;
452  mutex_exit(&(pool->mutex));
453 
454  ut_ad(mem_pool_validate(pool));
455 
456  *psize = ut_2_exp(n) - MEM_AREA_EXTRA_SIZE;
457  UNIV_MEM_ALLOC(MEM_AREA_EXTRA_SIZE + (byte*) area, *psize);
458 
459  return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*) area)));
460 }
461 
462 /********************************************************************/
465 UNIV_INLINE
466 mem_area_t*
468 /*===============*/
469  mem_area_t* area,
470  ulint size,
471  mem_pool_t* pool)
472 {
473  mem_area_t* buddy;
474 
475  ut_ad(size != 0);
476 
477  if (((((byte*) area) - pool->buf) % (2 * size)) == 0) {
478 
479  /* The buddy is in a higher address */
480 
481  buddy = (mem_area_t*)(((byte*) area) + size);
482 
483  if ((((byte*) buddy) - pool->buf) + size > pool->size) {
484 
485  /* The buddy is not wholly contained in the pool:
486  there is no buddy */
487 
488  buddy = NULL;
489  }
490  } else {
491  /* The buddy is in a lower address; NOTE that area cannot
492  be at the pool lower end, because then we would end up to
493  the upper branch in this if-clause: the remainder would be
494  0 */
495 
496  buddy = (mem_area_t*)(((byte*) area) - size);
497  }
498 
499  return(buddy);
500 }
501 
502 /********************************************************************/
504 UNIV_INTERN
505 void
507 /*==========*/
508  void* ptr,
510  mem_pool_t* pool)
511 {
512  mem_area_t* area;
513  mem_area_t* buddy;
514  void* new_ptr;
515  ulint size;
516  ulint n;
517 
518  if (UNIV_LIKELY(srv_use_sys_malloc)) {
519  free(ptr);
520 
521  return;
522  }
523 
524  /* It may be that the area was really allocated from the OS with
525  regular malloc: check if ptr points within our memory pool */
526 
527  if ((byte*) ptr < pool->buf || (byte*) ptr >= pool->buf + pool->size) {
528  ut_free(ptr);
529 
530  return;
531  }
532 
533  area = (mem_area_t*) (((byte*) ptr) - MEM_AREA_EXTRA_SIZE);
534 
535  if (mem_area_get_free(area)) {
536  fprintf(stderr,
537  "InnoDB: Error: Freeing element to mem pool"
538  " free list though the\n"
539  "InnoDB: element is marked free!\n");
540 
542  ut_error;
543  }
544 
545  size = mem_area_get_size(area);
546  UNIV_MEM_FREE(ptr, size - MEM_AREA_EXTRA_SIZE);
547 
548  if (size == 0) {
549  fprintf(stderr,
550  "InnoDB: Error: Mem area size is 0. Possibly a"
551  " memory overrun of the\n"
552  "InnoDB: previous allocated area!\n");
553 
555  ut_error;
556  }
557 
558 #ifdef UNIV_LIGHT_MEM_DEBUG
559  if (((byte*) area) + size < pool->buf + pool->size) {
560 
561  ulint next_size;
562 
563  next_size = mem_area_get_size(
564  (mem_area_t*)(((byte*) area) + size));
565  if (UNIV_UNLIKELY(!next_size || !ut_is_2pow(next_size))) {
566  fprintf(stderr,
567  "InnoDB: Error: Memory area size %lu,"
568  " next area size %lu not a power of 2!\n"
569  "InnoDB: Possibly a memory overrun of"
570  " the buffer being freed here.\n",
571  (ulong) size, (ulong) next_size);
573 
574  ut_error;
575  }
576  }
577 #endif
578  buddy = mem_area_get_buddy(area, size, pool);
579 
580  n = ut_2_log(size);
581 
582  mem_pool_mutex_enter(pool);
583  mem_n_threads_inside++;
584 
585  ut_a(mem_n_threads_inside == 1);
586 
587  if (buddy && mem_area_get_free(buddy)
588  && (size == mem_area_get_size(buddy))) {
589 
590  /* The buddy is in a free list */
591 
592  if ((byte*) buddy < (byte*) area) {
593  new_ptr = ((byte*) buddy) + MEM_AREA_EXTRA_SIZE;
594 
595  mem_area_set_size(buddy, 2 * size);
596  mem_area_set_free(buddy, FALSE);
597  } else {
598  new_ptr = ptr;
599 
600  mem_area_set_size(area, 2 * size);
601  }
602 
603  /* Remove the buddy from its free list and merge it to area */
604 
605  UT_LIST_REMOVE(free_list, pool->free_list[n], buddy);
606 
607  pool->reserved += ut_2_exp(n);
608 
609  mem_n_threads_inside--;
610  mem_pool_mutex_exit(pool);
611 
612  mem_area_free(new_ptr, pool);
613 
614  return;
615  } else {
616  UT_LIST_ADD_FIRST(free_list, pool->free_list[n], area);
617 
618  mem_area_set_free(area, TRUE);
619 
620  ut_ad(pool->reserved >= size);
621 
622  pool->reserved -= size;
623  }
624 
625  mem_n_threads_inside--;
626  mem_pool_mutex_exit(pool);
627 
628  ut_ad(mem_pool_validate(pool));
629 }
630 
631 /********************************************************************/
634 UNIV_INTERN
635 ibool
637 /*==============*/
638  mem_pool_t* pool)
639 {
640  mem_area_t* area;
641  mem_area_t* buddy;
642  ulint free;
643  ulint i;
644 
645  mem_pool_mutex_enter(pool);
646 
647  free = 0;
648 
649  for (i = 0; i < 64; i++) {
650 
651  UT_LIST_CHECK(free_list, mem_area_t, pool->free_list[i]);
652 
653  for (area = UT_LIST_GET_FIRST(pool->free_list[i]);
654  area != 0;
655  area = UT_LIST_GET_NEXT(free_list, area)) {
656 
657  ut_a(mem_area_get_free(area));
658  ut_a(mem_area_get_size(area) == ut_2_exp(i));
659 
660  buddy = mem_area_get_buddy(area, ut_2_exp(i), pool);
661 
662  ut_a(!buddy || !mem_area_get_free(buddy)
663  || (ut_2_exp(i) != mem_area_get_size(buddy)));
664 
665  free += ut_2_exp(i);
666  }
667  }
668 
669  ut_a(free + pool->reserved == pool->size);
670 
671  mem_pool_mutex_exit(pool);
672 
673  return(TRUE);
674 }
675 
676 /********************************************************************/
678 UNIV_INTERN
679 void
681 /*================*/
682  FILE* outfile,
683  mem_pool_t* pool)
684 {
685  ulint i;
686 
687  mem_pool_validate(pool);
688 
689  fprintf(outfile, "INFO OF A MEMORY POOL\n");
690 
691  mutex_enter(&(pool->mutex));
692 
693  for (i = 0; i < 64; i++) {
694  if (UT_LIST_GET_LEN(pool->free_list[i]) > 0) {
695 
696  fprintf(outfile,
697  "Free list length %lu for"
698  " blocks of size %lu\n",
699  (ulong) UT_LIST_GET_LEN(pool->free_list[i]),
700  (ulong) ut_2_exp(i));
701  }
702  }
703 
704  fprintf(outfile, "Pool size %lu, reserved %lu.\n", (ulong) pool->size,
705  (ulong) pool->reserved);
706  mutex_exit(&(pool->mutex));
707 }
708 
709 /********************************************************************/
712 UNIV_INTERN
713 ulint
715 /*==================*/
716  mem_pool_t* pool)
717 {
718  ulint reserved;
719 
720  mutex_enter(&(pool->mutex));
721 
722  reserved = pool->reserved;
723 
724  mutex_exit(&(pool->mutex));
725 
726  return(reserved);
727 }