~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/memory/root.cc

OK, Sun Studio still didn't like that...seems to think that inline means something different than other compilers think it is...

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
 
12
12
   You should have received a copy of the GNU General Public License
13
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
 
/**
17
 
 * @file
18
 
 * Routines to handle mallocing of results which will be freed the same time 
19
 
 */
20
 
 
21
 
#include <config.h>
22
 
 
23
 
#include <drizzled/internal/my_sys.h>
24
 
#include <drizzled/internal/m_string.h>
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
/* Routines to handle mallocing of results which will be freed the same time */
 
17
 
 
18
#include "config.h"
 
19
 
 
20
#include "drizzled/internal/my_sys.h"
 
21
#include "drizzled/internal/m_string.h"
25
22
 
26
23
#include <algorithm>
27
24
 
33
30
static const unsigned int MAX_BLOCK_TO_DROP= 4096;
34
31
static const unsigned int MAX_BLOCK_USAGE_BEFORE_DROP= 10;
35
32
 
36
 
/**
37
 
 * @brief
38
 
 * Initialize memory root
39
 
 *
40
 
 * @details
41
 
 * This function prepares memory root for further use, sets initial size of
42
 
 * chunk for memory allocation and pre-allocates first block if specified.
43
 
 * Altough error can happen during execution of this function if
44
 
 * pre_alloc_size is non-0 it won't be reported. Instead it will be
45
 
 * reported as error in first alloc_root() on this memory root.
46
 
 *
47
 
 * @param  mem_root       memory root to initialize
48
 
 * @param  block_size     size of chunks (blocks) used for memory allocation
49
 
 *                       (It is external size of chunk i.e. it should include
50
 
 *                      memory required for internal structures, thus it
51
 
 *                      should be no less than memory::ROOT_MIN_BLOCK_SIZE)
52
 
 *
53
 
 */
54
 
void memory::Root::init_alloc_root(size_t block_size_arg)
55
 
{
56
 
  free= used= pre_alloc= 0;
57
 
  min_malloc= 32;
58
 
  block_size= block_size_arg - memory::ROOT_MIN_BLOCK_SIZE;
59
 
  error_handler= 0;
60
 
  block_num= 4;                 /* We shift this with >>2 */
61
 
  first_block_usage= 0;
62
 
}
63
 
 
64
 
memory::Root::~Root()
65
 
{
66
 
}
67
 
 
68
 
 
69
 
/**
70
 
 * @details
71
 
 * Function aligns and assigns new value to block size; then it tries to
72
 
 * reuse one of existing blocks as prealloc block, or malloc new one of
73
 
 * requested size. If no blocks can be reused, all unused blocks are freed
74
 
 * before allocation.
75
 
 *
76
 
 * @param  mem_root        memory root to change defaults of
77
 
 * @param  block_size      new value of block size. Must be greater or equal
78
 
 *                         than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
79
 
 *                         68 bytes and depends on platform and compilation flags)
80
 
 * @param pre_alloc_size  new size of preallocated block. If not zero,
81
 
 *                        must be equal to or greater than block size,
82
 
 *                        otherwise means 'no prealloc'.
83
 
 */
84
 
void memory::Root::reset_root_defaults(size_t block_size_arg, size_t pre_alloc_size)
85
 
{
86
 
  block_size= block_size_arg - memory::ROOT_MIN_BLOCK_SIZE;
 
33
/*
 
34
  Initialize memory root
 
35
 
 
36
  SYNOPSIS
 
37
    memory::init_alloc_root()
 
38
      mem_root       - memory root to initialize
 
39
      block_size     - size of chunks (blocks) used for memory allocation
 
40
                       (It is external size of chunk i.e. it should include
 
41
                        memory required for internal structures, thus it
 
42
                        should be no less than memory::ROOT_MIN_BLOCK_SIZE)
 
43
 
 
44
  DESCRIPTION
 
45
    This function prepares memory root for further use, sets initial size of
 
46
    chunk for memory allocation and pre-allocates first block if specified.
 
47
    Altough error can happen during execution of this function if
 
48
    pre_alloc_size is non-0 it won't be reported. Instead it will be
 
49
    reported as error in first alloc_root() on this memory root.
 
50
*/
 
51
 
 
52
void memory::init_alloc_root(memory::Root *mem_root, size_t block_size)
 
53
{
 
54
  mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
 
55
  mem_root->min_malloc= 32;
 
56
  mem_root->block_size= block_size - memory::ROOT_MIN_BLOCK_SIZE;
 
57
  mem_root->error_handler= 0;
 
58
  mem_root->block_num= 4;                       /* We shift this with >>2 */
 
59
  mem_root->first_block_usage= 0;
 
60
 
 
61
  return;
 
62
}
 
63
 
 
64
 
 
65
/*
 
66
  SYNOPSIS
 
67
    reset_root_defaults()
 
68
    mem_root        memory root to change defaults of
 
69
    block_size      new value of block size. Must be greater or equal
 
70
                    than ALLOC_ROOT_MIN_BLOCK_SIZE (this value is about
 
71
                    68 bytes and depends on platform and compilation flags)
 
72
    pre_alloc_size  new size of preallocated block. If not zero,
 
73
                    must be equal to or greater than block size,
 
74
                    otherwise means 'no prealloc'.
 
75
  DESCRIPTION
 
76
    Function aligns and assigns new value to block size; then it tries to
 
77
    reuse one of existing blocks as prealloc block, or malloc new one of
 
78
    requested size. If no blocks can be reused, all unused blocks are freed
 
79
    before allocation.
 
80
*/
 
81
 
 
82
void memory::reset_root_defaults(memory::Root *mem_root, size_t block_size,
 
83
                                 size_t pre_alloc_size)
 
84
{
 
85
  assert(alloc_root_inited(mem_root));
 
86
 
 
87
  mem_root->block_size= block_size - memory::ROOT_MIN_BLOCK_SIZE;
87
88
  if (pre_alloc_size)
88
89
  {
89
90
    size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
90
 
    if (not pre_alloc || pre_alloc->size != size)
 
91
    if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size)
91
92
    {
92
 
      memory::internal::UsedMemory *mem, **prev= &this->free;
 
93
      memory::internal::UsedMemory *mem, **prev= &mem_root->free;
93
94
      /*
94
95
        Free unused blocks, so that consequent calls
95
96
        to reset_root_defaults won't eat away memory.
100
101
        if (mem->size == size)
101
102
        {
102
103
          /* We found a suitable block, no need to do anything else */
103
 
          pre_alloc= mem;
 
104
          mem_root->pre_alloc= mem;
104
105
          return;
105
106
        }
106
107
        if (mem->left + ALIGN_SIZE(sizeof(memory::internal::UsedMemory)) == mem->size)
107
108
        {
108
109
          /* remove block from the list and free it */
109
110
          *prev= mem->next;
110
 
          std::free(mem);
 
111
          free(mem);
111
112
        }
112
113
        else
113
114
          prev= &mem->next;
118
119
        mem->size= size;
119
120
        mem->left= pre_alloc_size;
120
121
        mem->next= *prev;
121
 
        *prev= pre_alloc= mem;
 
122
        *prev= mem_root->pre_alloc= mem;
122
123
      }
123
124
      else
124
125
      {
125
 
        pre_alloc= 0;
 
126
        mem_root->pre_alloc= 0;
126
127
      }
127
128
    }
128
129
  }
129
130
  else
130
131
  {
131
 
    pre_alloc= 0;
 
132
    mem_root->pre_alloc= 0;
132
133
  }
133
134
}
134
135
 
135
 
/**
136
 
 * @brief 
137
 
 * Allocate a chunk of memory from the Root structure provided, 
138
 
 * obtaining more memory from the heap if necessary
139
 
 *
140
 
 * @pre
141
 
 * mem_root must have been initialised via init_alloc_root()
142
 
 *
143
 
 * @param  mem_root  The memory Root to allocate from
144
 
 * @param  length    The size of the block to allocate
145
 
 *
146
 
 * @todo Would this be more suitable as a member function on the
147
 
 * Root class?
148
 
 */
149
 
void *memory::Root::alloc_root(size_t length)
 
136
 
 
137
void *memory::alloc_root(memory::Root *mem_root, size_t length)
150
138
{
 
139
  size_t get_size, block_size;
151
140
  unsigned char* point;
152
141
  memory::internal::UsedMemory *next= NULL;
153
142
  memory::internal::UsedMemory **prev;
154
 
  assert(alloc_root_inited());
 
143
  assert(alloc_root_inited(mem_root));
155
144
 
156
145
  length= ALIGN_SIZE(length);
157
 
  if ((*(prev= &this->free)) != NULL)
 
146
  if ((*(prev= &mem_root->free)) != NULL)
158
147
  {
159
148
    if ((*prev)->left < length &&
160
 
        this->first_block_usage++ >= MAX_BLOCK_USAGE_BEFORE_DROP &&
 
149
        mem_root->first_block_usage++ >= MAX_BLOCK_USAGE_BEFORE_DROP &&
161
150
        (*prev)->left < MAX_BLOCK_TO_DROP)
162
151
    {
163
152
      next= *prev;
164
153
      *prev= next->next;                        /* Remove block from list */
165
 
      next->next= this->used;
166
 
      this->used= next;
167
 
      this->first_block_usage= 0;
 
154
      next->next= mem_root->used;
 
155
      mem_root->used= next;
 
156
      mem_root->first_block_usage= 0;
168
157
    }
169
158
    for (next= *prev ; next && next->left < length ; next= next->next)
170
159
      prev= &next->next;
171
160
  }
172
161
  if (! next)
173
162
  {                                             /* Time to alloc new block */
174
 
    size_t get_size, tmp_block_size;
175
 
 
176
 
    tmp_block_size= this->block_size * (this->block_num >> 2);
 
163
    block_size= mem_root->block_size * (mem_root->block_num >> 2);
177
164
    get_size= length+ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
178
 
    get_size= max(get_size, tmp_block_size);
 
165
    get_size= max(get_size, block_size);
179
166
 
180
167
    if (!(next = static_cast<memory::internal::UsedMemory *>(malloc(get_size))))
181
168
    {
182
 
      if (this->error_handler)
183
 
        (*this->error_handler)();
 
169
      if (mem_root->error_handler)
 
170
        (*mem_root->error_handler)();
184
171
      return NULL;
185
172
    }
186
 
    this->block_num++;
 
173
    mem_root->block_num++;
187
174
    next->next= *prev;
188
175
    next->size= get_size;
189
176
    next->left= get_size-ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
191
178
  }
192
179
 
193
180
  point= (unsigned char*) ((char*) next+ (next->size-next->left));
194
 
  /** @todo next part may be unneeded due to this->first_block_usage counter*/
195
 
  if ((next->left-= length) < this->min_malloc)
 
181
  /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
 
182
  if ((next->left-= length) < mem_root->min_malloc)
196
183
  {                                             /* Full block */
197
184
    *prev= next->next;                          /* Remove block from list */
198
 
    next->next= this->used;
199
 
    this->used= next;
200
 
    this->first_block_usage= 0;
 
185
    next->next= mem_root->used;
 
186
    mem_root->used= next;
 
187
    mem_root->first_block_usage= 0;
201
188
  }
202
 
 
203
 
  return point;
 
189
  return((void*) point);
204
190
}
205
191
 
206
192
 
207
 
/**
208
 
 * @brief
209
 
 * Allocate many pointers at the same time.
210
 
 *
211
 
 * @details
212
 
 * The variable arguments are a list of alternating pointers and lengths,
213
 
 * terminated by a null pointer:
214
 
 * @li <tt>char ** pointer1</tt>
215
 
 * @li <tt>uint length1</tt>
216
 
 * @li <tt>char ** pointer2</tt>
217
 
 * @li <tt>uint length2</tt>
218
 
 * @li <tt>...</tt>
219
 
 * @li <tt>NULL</tt>
220
 
 *
221
 
 * @c pointer1, @c pointer2 etc. all point into big allocated memory area
222
 
 *
223
 
 * @param root  Memory root
224
 
 *
225
 
 * @return
226
 
 * A pointer to the beginning of the allocated memory block in case of 
227
 
 * success or NULL if out of memory
228
 
 */
229
 
void *memory::Root::multi_alloc_root(int unused, ...)
 
193
/*
 
194
  Allocate many pointers at the same time.
 
195
 
 
196
  DESCRIPTION
 
197
    ptr1, ptr2, etc all point into big allocated memory area.
 
198
 
 
199
  SYNOPSIS
 
200
    multi_alloc_root()
 
201
      root               Memory root
 
202
      ptr1, length1      Multiple arguments terminated by a NULL pointer
 
203
      ptr2, length2      ...
 
204
      ...
 
205
      NULL
 
206
 
 
207
  RETURN VALUE
 
208
    A pointer to the beginning of the allocated memory block
 
209
    in case of success or NULL if out of memory.
 
210
*/
 
211
 
 
212
void *memory::multi_alloc_root(memory::Root *root, ...)
230
213
{
231
214
  va_list args;
232
215
  char **ptr, *start, *res;
233
216
  size_t tot_length, length;
234
217
 
235
 
  (void)unused; // For some reason Sun Studio registers unused as not used.
236
 
  va_start(args, unused);
 
218
  va_start(args, root);
237
219
  tot_length= 0;
238
220
  while ((ptr= va_arg(args, char **)))
239
221
  {
242
224
  }
243
225
  va_end(args);
244
226
 
245
 
  if (!(start= (char*) this->alloc_root(tot_length)))
 
227
  if (!(start= (char*) memory::alloc_root(root, tot_length)))
246
228
    return(0);
247
229
 
248
 
  va_start(args, unused);
 
230
  va_start(args, root);
249
231
  res= start;
250
232
  while ((ptr= va_arg(args, char **)))
251
233
  {
257
239
  return((void*) start);
258
240
}
259
241
 
260
 
static void trash_mem(memory::internal::UsedMemory *)
261
 
{
262
 
  TRASH(((char*)(x) + (x->size - x->left)), x->left);
263
 
}
264
 
 
265
 
/**
266
 
 * @brief
267
 
 * Mark all data in blocks free for reusage 
268
 
 */
269
 
void memory::Root::mark_blocks_free()
 
242
#define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left)
 
243
 
 
244
/* Mark all data in blocks free for reusage */
 
245
 
 
246
static inline void mark_blocks_free(memory::Root* root)
270
247
{
271
248
  memory::internal::UsedMemory *next;
272
249
  memory::internal::UsedMemory **last;
273
250
 
274
251
  /* iterate through (partially) free blocks, mark them free */
275
 
  last= &free;
276
 
  for (next= free; next; next= *(last= &next->next))
 
252
  last= &root->free;
 
253
  for (next= root->free; next; next= *(last= &next->next))
277
254
  {
278
255
    next->left= next->size - ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
279
 
    trash_mem(next);
 
256
    TRASH_MEM(next);
280
257
  }
281
258
 
282
259
  /* Combine the free and the used list */
283
 
  *last= next= used;
 
260
  *last= next=root->used;
284
261
 
285
262
  /* now go through the used blocks and mark them free */
286
263
  for (; next; next= next->next)
287
264
  {
288
265
    next->left= next->size - ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
289
 
    trash_mem(next);
 
266
    TRASH_MEM(next);
290
267
  }
291
268
 
292
269
  /* Now everything is set; Indicate that nothing is used anymore */
293
 
  used= 0;
294
 
  first_block_usage= 0;
 
270
  root->used= 0;
 
271
  root->first_block_usage= 0;
295
272
}
296
273
 
297
 
/**
298
 
 * @brief
299
 
 * Deallocate everything used by memory::alloc_root or just move
300
 
 * used blocks to free list if called with MY_USED_TO_FREE
301
 
 *
302
 
 * @note
303
 
 * One can call this function either with root block initialised with
304
 
 * init_alloc_root() or with a zero:ed block.
305
 
 * It's also safe to call this multiple times with the same mem_root.
306
 
 *
307
 
 * @param   root     Memory root
308
 
 * @param   MyFlags  Flags for what should be freed:
309
 
 *   @li   MARK_BLOCKS_FREED    Don't free blocks, just mark them free
310
 
 *   @li   KEEP_PREALLOC        If this is not set, then free also the
311
 
 *                              preallocated block
312
 
 */
313
 
void memory::Root::free_root(myf MyFlags)
 
274
 
 
275
/*
 
276
  Deallocate everything used by memory::alloc_root or just move
 
277
  used blocks to free list if called with MY_USED_TO_FREE
 
278
 
 
279
  SYNOPSIS
 
280
    free_root()
 
281
      root              Memory root
 
282
      MyFlags           Flags for what should be freed:
 
283
 
 
284
        MARK_BLOCKS_FREED       Don't free blocks, just mark them free
 
285
        KEEP_PREALLOC           If this is not set, then free also the
 
286
                                preallocated block
 
287
 
 
288
  NOTES
 
289
    One can call this function either with root block initialised with
 
290
    init_alloc_root() or with a zero:ed block.
 
291
    It's also safe to call this multiple times with the same mem_root.
 
292
*/
 
293
 
 
294
void memory::free_root(memory::Root *root, myf MyFlags)
314
295
{
315
296
  memory::internal::UsedMemory *next,*old;
316
297
 
317
298
  if (MyFlags & memory::MARK_BLOCKS_FREE)
318
299
  {
319
 
    this->mark_blocks_free();
 
300
    mark_blocks_free(root);
320
301
    return;
321
302
  }
322
303
  if (!(MyFlags & memory::KEEP_PREALLOC))
323
 
    this->pre_alloc=0;
 
304
    root->pre_alloc=0;
324
305
 
325
 
  for (next=this->used; next ;)
 
306
  for (next=root->used; next ;)
326
307
  {
327
308
    old=next; next= next->next ;
328
 
    if (old != this->pre_alloc)
329
 
      std::free(old);
 
309
    if (old != root->pre_alloc)
 
310
      free(old);
330
311
  }
331
 
  for (next=this->free ; next ;)
 
312
  for (next=root->free ; next ;)
332
313
  {
333
314
    old=next; next= next->next;
334
 
    if (old != this->pre_alloc)
335
 
      std::free(old);
336
 
  }
337
 
  this->used=this->free=0;
338
 
  if (this->pre_alloc)
339
 
  {
340
 
    this->free=this->pre_alloc;
341
 
    this->free->left=this->pre_alloc->size-ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
342
 
    trash_mem(this->pre_alloc);
343
 
    this->free->next=0;
344
 
  }
345
 
  this->block_num= 4;
346
 
  this->first_block_usage= 0;
347
 
}
348
 
 
349
 
/**
350
 
 * @brief
351
 
 * Duplicate a null-terminated string into memory allocated from within the
352
 
 * specified Root
353
 
 */
354
 
char *memory::Root::strdup_root(const char *str)
355
 
{
356
 
  return strmake_root(str, strlen(str));
357
 
}
358
 
 
359
 
/**
360
 
 * @brief
361
 
 * Copy the (not necessarily null-terminated) string into memory allocated
362
 
 * from within the specified Root
363
 
 *
364
 
 * @details
365
 
 * Note that the string is copied according to the length specified, so
366
 
 * null-termination is ignored. The duplicated string will be null-terminated,
367
 
 * even if the original string wasn't (one additional byte is allocated for
368
 
 * this purpose).
369
 
 */
370
 
char *memory::Root::strmake_root(const char *str, size_t len)
 
315
    if (old != root->pre_alloc)
 
316
      free(old);
 
317
  }
 
318
  root->used=root->free=0;
 
319
  if (root->pre_alloc)
 
320
  {
 
321
    root->free=root->pre_alloc;
 
322
    root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
 
323
    TRASH_MEM(root->pre_alloc);
 
324
    root->free->next=0;
 
325
  }
 
326
  root->block_num= 4;
 
327
  root->first_block_usage= 0;
 
328
  return;
 
329
}
 
330
 
 
331
/*
 
332
  Find block that contains an object and set the pre_alloc to it
 
333
*/
 
334
 
 
335
void memory::set_prealloc_root(memory::Root *root, char *ptr)
 
336
{
 
337
  memory::internal::UsedMemory *next;
 
338
  for (next=root->used; next ; next=next->next)
 
339
  {
 
340
    if ((char*) next <= ptr && (char*) next + next->size > ptr)
 
341
    {
 
342
      root->pre_alloc=next;
 
343
      return;
 
344
    }
 
345
  }
 
346
  for (next=root->free ; next ; next=next->next)
 
347
  {
 
348
    if ((char*) next <= ptr && (char*) next + next->size > ptr)
 
349
    {
 
350
      root->pre_alloc=next;
 
351
      return;
 
352
    }
 
353
  }
 
354
}
 
355
 
 
356
 
 
357
char *memory::strdup_root(memory::Root *root, const char *str)
 
358
{
 
359
  return strmake_root(root, str, strlen(str));
 
360
}
 
361
 
 
362
 
 
363
char *memory::strmake_root(memory::Root *root, const char *str, size_t len)
371
364
{
372
365
  char *pos;
373
 
  if ((pos= (char *)alloc_root(len+1)))
 
366
  if ((pos=(char *)memory::alloc_root(root,len+1)))
374
367
  {
375
368
    memcpy(pos,str,len);
376
369
    pos[len]=0;
378
371
  return pos;
379
372
}
380
373
 
381
 
/**
382
 
 * @brief
383
 
 * Duplicate the provided block into memory allocated from within the specified
384
 
 * Root
385
 
 *
386
 
 * @return
387
 
 * non-NULL pointer to a copy of the data if memory could be allocated, otherwise
388
 
 * NULL
389
 
 */
390
 
void *memory::Root::memdup_root(const void *str, size_t len)
 
374
 
 
375
void *memory::memdup_root(memory::Root *root, const void *str, size_t len)
391
376
{
392
377
  void *pos;
393
 
 
394
 
  if ((pos= this->alloc_root(len)))
 
378
  if ((pos=memory::alloc_root(root,len)))
395
379
    memcpy(pos,str,len);
396
 
 
397
380
  return pos;
398
381
}
399
382