~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/ut/ut0mem.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/************************************************************************
 
2
Memory primitives
 
3
 
 
4
(c) 1994, 1995 Innobase Oy
 
5
 
 
6
Created 5/11/1994 Heikki Tuuri
 
7
*************************************************************************/
 
8
 
 
9
#include "ut0mem.h"
 
10
 
 
11
#ifdef UNIV_NONINL
 
12
#include "ut0mem.ic"
 
13
#endif
 
14
 
 
15
#include "mem0mem.h"
 
16
#include "os0sync.h"
 
17
#include "os0thread.h"
 
18
 
 
19
/* This struct is placed first in every allocated memory block */
 
20
typedef struct ut_mem_block_struct ut_mem_block_t;
 
21
 
 
22
/* The total amount of memory currently allocated from the OS with malloc */
 
23
ulint   ut_total_allocated_memory       = 0;
 
24
 
 
25
struct ut_mem_block_struct{
 
26
        UT_LIST_NODE_T(ut_mem_block_t) mem_block_list;
 
27
                        /* mem block list node */
 
28
        ulint   size;   /* size of allocated memory */
 
29
        ulint   magic_n;
 
30
};
 
31
 
 
32
#define UT_MEM_MAGIC_N  1601650166
 
33
 
 
34
/* List of all memory blocks allocated from the operating system
 
35
with malloc */
 
36
UT_LIST_BASE_NODE_T(ut_mem_block_t)   ut_mem_block_list;
 
37
 
 
38
os_fast_mutex_t ut_list_mutex;  /* this protects the list */
 
39
 
 
40
ibool  ut_mem_block_list_inited = FALSE;
 
41
 
 
42
ulint*  ut_mem_null_ptr = NULL;
 
43
 
 
44
/**************************************************************************
 
45
Initializes the mem block list at database startup. */
 
46
static
 
47
void
 
48
ut_mem_block_list_init(void)
 
49
/*========================*/
 
50
{
 
51
        os_fast_mutex_init(&ut_list_mutex);
 
52
        UT_LIST_INIT(ut_mem_block_list);
 
53
        ut_mem_block_list_inited = TRUE;
 
54
}
 
55
 
 
56
/**************************************************************************
 
57
Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is
 
58
defined and set_to_zero is TRUE. */
 
59
 
 
60
void*
 
61
ut_malloc_low(
 
62
/*==========*/
 
63
                                /* out, own: allocated memory */
 
64
        ulint   n,              /* in: number of bytes to allocate */
 
65
        ibool   set_to_zero,    /* in: TRUE if allocated memory should be
 
66
                                set to zero if UNIV_SET_MEM_TO_ZERO is
 
67
                                defined */
 
68
        ibool   assert_on_error)/* in: if TRUE, we crash mysqld if the
 
69
                                memory cannot be allocated */
 
70
{
 
71
        ulint   retry_count     = 0;
 
72
        void*   ret;
 
73
 
 
74
        ut_ad((sizeof(ut_mem_block_t) % 8) == 0); /* check alignment ok */
 
75
 
 
76
        if (!ut_mem_block_list_inited) {
 
77
                ut_mem_block_list_init();
 
78
        }
 
79
retry:
 
80
        os_fast_mutex_lock(&ut_list_mutex);
 
81
 
 
82
        ret = malloc(n + sizeof(ut_mem_block_t));
 
83
 
 
84
        if (ret == NULL && retry_count < 60) {
 
85
                if (retry_count == 0) {
 
86
                        ut_print_timestamp(stderr);
 
87
 
 
88
                        fprintf(stderr,
 
89
                                "  InnoDB: Error: cannot allocate"
 
90
                                " %lu bytes of\n"
 
91
                                "InnoDB: memory with malloc!"
 
92
                                " Total allocated memory\n"
 
93
                                "InnoDB: by InnoDB %lu bytes."
 
94
                                " Operating system errno: %lu\n"
 
95
                                "InnoDB: Check if you should"
 
96
                                " increase the swap file or\n"
 
97
                                "InnoDB: ulimits of your operating system.\n"
 
98
                                "InnoDB: On FreeBSD check you"
 
99
                                " have compiled the OS with\n"
 
100
                                "InnoDB: a big enough maximum process size.\n"
 
101
                                "InnoDB: Note that in most 32-bit"
 
102
                                " computers the process\n"
 
103
                                "InnoDB: memory space is limited"
 
104
                                " to 2 GB or 4 GB.\n"
 
105
                                "InnoDB: We keep retrying"
 
106
                                " the allocation for 60 seconds...\n",
 
107
                                (ulong) n, (ulong) ut_total_allocated_memory,
 
108
#ifdef __WIN__
 
109
                                (ulong) GetLastError()
 
110
#else
 
111
                                (ulong) errno
 
112
#endif
 
113
                                );
 
114
                }
 
115
 
 
116
                os_fast_mutex_unlock(&ut_list_mutex);
 
117
 
 
118
                /* Sleep for a second and retry the allocation; maybe this is
 
119
                just a temporary shortage of memory */
 
120
 
 
121
                os_thread_sleep(1000000);
 
122
 
 
123
                retry_count++;
 
124
 
 
125
                goto retry;
 
126
        }
 
127
 
 
128
        if (ret == NULL) {
 
129
                /* Flush stderr to make more probable that the error
 
130
                message gets in the error file before we generate a seg
 
131
                fault */
 
132
 
 
133
                fflush(stderr);
 
134
 
 
135
                os_fast_mutex_unlock(&ut_list_mutex);
 
136
 
 
137
                /* Make an intentional seg fault so that we get a stack
 
138
                trace */
 
139
                /* Intentional segfault on NetWare causes an abend. Avoid this
 
140
                by graceful exit handling in ut_a(). */
 
141
#if (!defined __NETWARE__)
 
142
                if (assert_on_error) {
 
143
                        ut_print_timestamp(stderr);
 
144
 
 
145
                        fprintf(stderr,
 
146
                                "  InnoDB: We now intentionally"
 
147
                                " generate a seg fault so that\n"
 
148
                                "InnoDB: on Linux we get a stack trace.\n");
 
149
 
 
150
                        if (*ut_mem_null_ptr) ut_mem_null_ptr = 0;
 
151
                } else {
 
152
                        return(NULL);
 
153
                }
 
154
#else
 
155
                ut_a(0);
 
156
#endif
 
157
        }
 
158
 
 
159
        if (set_to_zero) {
 
160
#ifdef UNIV_SET_MEM_TO_ZERO
 
161
                memset(ret, '\0', n + sizeof(ut_mem_block_t));
 
162
#endif
 
163
        }
 
164
 
 
165
        UNIV_MEM_ALLOC(ret, n + sizeof(ut_mem_block_t));
 
166
 
 
167
        ((ut_mem_block_t*)ret)->size = n + sizeof(ut_mem_block_t);
 
168
        ((ut_mem_block_t*)ret)->magic_n = UT_MEM_MAGIC_N;
 
169
 
 
170
        ut_total_allocated_memory += n + sizeof(ut_mem_block_t);
 
171
 
 
172
        UT_LIST_ADD_FIRST(mem_block_list, ut_mem_block_list,
 
173
                          ((ut_mem_block_t*)ret));
 
174
        os_fast_mutex_unlock(&ut_list_mutex);
 
175
 
 
176
        return((void*)((byte*)ret + sizeof(ut_mem_block_t)));
 
177
}
 
178
 
 
179
/**************************************************************************
 
180
Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is
 
181
defined. */
 
182
 
 
183
void*
 
184
ut_malloc(
 
185
/*======*/
 
186
                        /* out, own: allocated memory */
 
187
        ulint   n)      /* in: number of bytes to allocate */
 
188
{
 
189
        return(ut_malloc_low(n, TRUE, TRUE));
 
190
}
 
191
 
 
192
/**************************************************************************
 
193
Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs
 
194
out. It cannot be used if we want to return an error message. Prints to
 
195
stderr a message if fails. */
 
196
 
 
197
ibool
 
198
ut_test_malloc(
 
199
/*===========*/
 
200
                        /* out: TRUE if succeeded */
 
201
        ulint   n)      /* in: try to allocate this many bytes */
 
202
{
 
203
        void*   ret;
 
204
 
 
205
        ret = malloc(n);
 
206
 
 
207
        if (ret == NULL) {
 
208
                ut_print_timestamp(stderr);
 
209
                fprintf(stderr,
 
210
                        "  InnoDB: Error: cannot allocate"
 
211
                        " %lu bytes of memory for\n"
 
212
                        "InnoDB: a BLOB with malloc! Total allocated memory\n"
 
213
                        "InnoDB: by InnoDB %lu bytes."
 
214
                        " Operating system errno: %d\n"
 
215
                        "InnoDB: Check if you should increase"
 
216
                        " the swap file or\n"
 
217
                        "InnoDB: ulimits of your operating system.\n"
 
218
                        "InnoDB: On FreeBSD check you have"
 
219
                        " compiled the OS with\n"
 
220
                        "InnoDB: a big enough maximum process size.\n",
 
221
                        (ulong) n,
 
222
                        (ulong) ut_total_allocated_memory,
 
223
                        (int) errno);
 
224
                return(FALSE);
 
225
        }
 
226
 
 
227
        free(ret);
 
228
 
 
229
        return(TRUE);
 
230
}
 
231
 
 
232
/**************************************************************************
 
233
Frees a memory block allocated with ut_malloc. */
 
234
 
 
235
void
 
236
ut_free(
 
237
/*====*/
 
238
        void* ptr)  /* in, own: memory block */
 
239
{
 
240
        ut_mem_block_t* block;
 
241
 
 
242
        block = (ut_mem_block_t*)((byte*)ptr - sizeof(ut_mem_block_t));
 
243
 
 
244
        os_fast_mutex_lock(&ut_list_mutex);
 
245
 
 
246
        ut_a(block->magic_n == UT_MEM_MAGIC_N);
 
247
        ut_a(ut_total_allocated_memory >= block->size);
 
248
 
 
249
        ut_total_allocated_memory -= block->size;
 
250
 
 
251
        UT_LIST_REMOVE(mem_block_list, ut_mem_block_list, block);
 
252
        free(block);
 
253
 
 
254
        os_fast_mutex_unlock(&ut_list_mutex);
 
255
}
 
256
 
 
257
/**************************************************************************
 
258
Implements realloc. This is needed by /pars/lexyy.c. Otherwise, you should not
 
259
use this function because the allocation functions in mem0mem.h are the
 
260
recommended ones in InnoDB.
 
261
 
 
262
man realloc in Linux, 2004:
 
263
 
 
264
       realloc()  changes the size of the memory block pointed to
 
265
       by ptr to size bytes.  The contents will be  unchanged  to
 
266
       the minimum of the old and new sizes; newly allocated memďż˝
 
267
       ory will be uninitialized.  If ptr is NULL,  the  call  is
 
268
       equivalent  to malloc(size); if size is equal to zero, the
 
269
       call is equivalent to free(ptr).  Unless ptr is  NULL,  it
 
270
       must  have  been  returned by an earlier call to malloc(),
 
271
       calloc() or realloc().
 
272
 
 
273
RETURN VALUE
 
274
       realloc() returns a pointer to the newly allocated memory,
 
275
       which is suitably aligned for any kind of variable and may
 
276
       be different from ptr, or NULL if the  request  fails.  If
 
277
       size  was equal to 0, either NULL or a pointer suitable to
 
278
       be passed to free() is returned.  If realloc()  fails  the
 
279
       original  block  is  left  untouched  - it is not freed or
 
280
       moved. */
 
281
 
 
282
void*
 
283
ut_realloc(
 
284
/*=======*/
 
285
                        /* out, own: pointer to new mem block or NULL */
 
286
        void*   ptr,    /* in: pointer to old block or NULL */
 
287
        ulint   size)   /* in: desired size */
 
288
{
 
289
        ut_mem_block_t* block;
 
290
        ulint           old_size;
 
291
        ulint           min_size;
 
292
        void*           new_ptr;
 
293
 
 
294
        if (ptr == NULL) {
 
295
 
 
296
                return(ut_malloc(size));
 
297
        }
 
298
 
 
299
        if (size == 0) {
 
300
                ut_free(ptr);
 
301
 
 
302
                return(NULL);
 
303
        }
 
304
 
 
305
        block = (ut_mem_block_t*)((byte*)ptr - sizeof(ut_mem_block_t));
 
306
 
 
307
        ut_a(block->magic_n == UT_MEM_MAGIC_N);
 
308
 
 
309
        old_size = block->size - sizeof(ut_mem_block_t);
 
310
 
 
311
        if (size < old_size) {
 
312
                min_size = size;
 
313
        } else {
 
314
                min_size = old_size;
 
315
        }
 
316
 
 
317
        new_ptr = ut_malloc(size);
 
318
 
 
319
        if (new_ptr == NULL) {
 
320
 
 
321
                return(NULL);
 
322
        }
 
323
 
 
324
        /* Copy the old data from ptr */
 
325
        ut_memcpy(new_ptr, ptr, min_size);
 
326
 
 
327
        ut_free(ptr);
 
328
 
 
329
        return(new_ptr);
 
330
}
 
331
 
 
332
/**************************************************************************
 
333
Frees in shutdown all allocated memory not freed yet. */
 
334
 
 
335
void
 
336
ut_free_all_mem(void)
 
337
/*=================*/
 
338
{
 
339
        ut_mem_block_t* block;
 
340
 
 
341
        os_fast_mutex_free(&ut_list_mutex);
 
342
 
 
343
        while ((block = UT_LIST_GET_FIRST(ut_mem_block_list))) {
 
344
 
 
345
                ut_a(block->magic_n == UT_MEM_MAGIC_N);
 
346
                ut_a(ut_total_allocated_memory >= block->size);
 
347
 
 
348
                ut_total_allocated_memory -= block->size;
 
349
 
 
350
                UT_LIST_REMOVE(mem_block_list, ut_mem_block_list, block);
 
351
                free(block);
 
352
        }
 
353
 
 
354
        if (ut_total_allocated_memory != 0) {
 
355
                fprintf(stderr,
 
356
                        "InnoDB: Warning: after shutdown"
 
357
                        " total allocated memory is %lu\n",
 
358
                        (ulong) ut_total_allocated_memory);
 
359
        }
 
360
}
 
361
 
 
362
/**************************************************************************
 
363
Copies up to size - 1 characters from the NUL-terminated string src to
 
364
dst, NUL-terminating the result. Returns strlen(src), so truncation
 
365
occurred if the return value >= size. */
 
366
 
 
367
ulint
 
368
ut_strlcpy(
 
369
/*=======*/
 
370
                                /* out: strlen(src) */
 
371
        char*           dst,    /* in: destination buffer */
 
372
        const char*     src,    /* in: source buffer */
 
373
        ulint           size)   /* in: size of destination buffer */
 
374
{
 
375
        ulint   src_size = strlen(src);
 
376
 
 
377
        if (size != 0) {
 
378
                ulint   n = ut_min(src_size, size - 1);
 
379
 
 
380
                memcpy(dst, src, n);
 
381
                dst[n] = '\0';
 
382
        }
 
383
 
 
384
        return(src_size);
 
385
}
 
386
 
 
387
/**************************************************************************
 
388
Like ut_strlcpy, but if src doesn't fit in dst completely, copies the last
 
389
(size - 1) bytes of src, not the first. */
 
390
 
 
391
ulint
 
392
ut_strlcpy_rev(
 
393
/*===========*/
 
394
                                /* out: strlen(src) */
 
395
        char*           dst,    /* in: destination buffer */
 
396
        const char*     src,    /* in: source buffer */
 
397
        ulint           size)   /* in: size of destination buffer */
 
398
{
 
399
        ulint   src_size = strlen(src);
 
400
 
 
401
        if (size != 0) {
 
402
                ulint   n = ut_min(src_size, size - 1);
 
403
 
 
404
                memcpy(dst, src + src_size - n, n + 1);
 
405
        }
 
406
 
 
407
        return(src_size);
 
408
}
 
409
 
 
410
/**************************************************************************
 
411
Make a quoted copy of a NUL-terminated string.  Leading and trailing
 
412
quotes will not be included; only embedded quotes will be escaped.
 
413
See also ut_strlenq() and ut_memcpyq(). */
 
414
 
 
415
char*
 
416
ut_strcpyq(
 
417
/*=======*/
 
418
                                /* out: pointer to end of dest */
 
419
        char*           dest,   /* in: output buffer */
 
420
        char            q,      /* in: the quote character */
 
421
        const char*     src)    /* in: null-terminated string */
 
422
{
 
423
        while (*src) {
 
424
                if ((*dest++ = *src++) == q) {
 
425
                        *dest++ = q;
 
426
                }
 
427
        }
 
428
 
 
429
        return(dest);
 
430
}
 
431
 
 
432
/**************************************************************************
 
433
Make a quoted copy of a fixed-length string.  Leading and trailing
 
434
quotes will not be included; only embedded quotes will be escaped.
 
435
See also ut_strlenq() and ut_strcpyq(). */
 
436
 
 
437
char*
 
438
ut_memcpyq(
 
439
/*=======*/
 
440
                                /* out: pointer to end of dest */
 
441
        char*           dest,   /* in: output buffer */
 
442
        char            q,      /* in: the quote character */
 
443
        const char*     src,    /* in: string to be quoted */
 
444
        ulint           len)    /* in: length of src */
 
445
{
 
446
        const char*     srcend = src + len;
 
447
 
 
448
        while (src < srcend) {
 
449
                if ((*dest++ = *src++) == q) {
 
450
                        *dest++ = q;
 
451
                }
 
452
        }
 
453
 
 
454
        return(dest);
 
455
}
 
456
 
 
457
/**************************************************************************
 
458
Return the number of times s2 occurs in s1. Overlapping instances of s2
 
459
are only counted once. */
 
460
 
 
461
ulint
 
462
ut_strcount(
 
463
/*========*/
 
464
                                /* out: the number of times s2 occurs in s1 */
 
465
        const char*     s1,     /* in: string to search in */
 
466
        const char*     s2)     /* in: string to search for */
 
467
{
 
468
        ulint   count = 0;
 
469
        ulint   len = strlen(s2);
 
470
 
 
471
        if (len == 0) {
 
472
 
 
473
                return(0);
 
474
        }
 
475
 
 
476
        for (;;) {
 
477
                s1 = strstr(s1, s2);
 
478
 
 
479
                if (!s1) {
 
480
 
 
481
                        break;
 
482
                }
 
483
 
 
484
                count++;
 
485
                s1 += len;
 
486
        }
 
487
 
 
488
        return(count);
 
489
}
 
490
 
 
491
/**************************************************************************
 
492
Replace every occurrence of s1 in str with s2. Overlapping instances of s1
 
493
are only replaced once. */
 
494
 
 
495
char *
 
496
ut_strreplace(
 
497
/*==========*/
 
498
                                /* out, own: modified string, must be
 
499
                                freed with mem_free() */
 
500
        const char*     str,    /* in: string to operate on */
 
501
        const char*     s1,     /* in: string to replace */
 
502
        const char*     s2)     /* in: string to replace s1 with */
 
503
{
 
504
        char*           new_str;
 
505
        char*           ptr;
 
506
        const char*     str_end;
 
507
        ulint           str_len = strlen(str);
 
508
        ulint           s1_len = strlen(s1);
 
509
        ulint           s2_len = strlen(s2);
 
510
        ulint           count = 0;
 
511
        int             len_delta = (int)s2_len - (int)s1_len;
 
512
 
 
513
        str_end = str + str_len;
 
514
 
 
515
        if (len_delta <= 0) {
 
516
                len_delta = 0;
 
517
        } else {
 
518
                count = ut_strcount(str, s1);
 
519
        }
 
520
 
 
521
        new_str = mem_alloc(str_len + count * len_delta + 1);
 
522
        ptr = new_str;
 
523
 
 
524
        while (str) {
 
525
                const char*     next = strstr(str, s1);
 
526
 
 
527
                if (!next) {
 
528
                        next = str_end;
 
529
                }
 
530
 
 
531
                memcpy(ptr, str, next - str);
 
532
                ptr += next - str;
 
533
 
 
534
                if (next == str_end) {
 
535
 
 
536
                        break;
 
537
                }
 
538
 
 
539
                memcpy(ptr, s2, s2_len);
 
540
                ptr += s2_len;
 
541
 
 
542
                str = next + s1_len;
 
543
        }
 
544
 
 
545
        *ptr = '\0';
 
546
 
 
547
        return(new_str);
 
548
}