~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/memory/root.cc

  • Committer: Stewart Smith
  • Date: 2010-11-03 03:27:09 UTC
  • mto: (1902.1.1 build) (1910.1.2 build)
  • mto: This revision was merged to the branch mainline in revision 1903.
  • Revision ID: stewart@flamingspork.com-20101103032709-oyvfrc6eb8fzj0mr
fix docs warning: docs/unlock.rst:2: (WARNING/2) Title underline too short.

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
14
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
15
15
 
16
 
/* Routines to handle mallocing of results which will be freed the same time */
 
16
/**
 
17
 * @file
 
18
 * Routines to handle mallocing of results which will be freed the same time 
 
19
 */
17
20
 
18
21
#include "config.h"
19
22
 
30
33
static const unsigned int MAX_BLOCK_TO_DROP= 4096;
31
34
static const unsigned int MAX_BLOCK_USAGE_BEFORE_DROP= 10;
32
35
 
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;
 
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;
88
87
  if (pre_alloc_size)
89
88
  {
90
89
    size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
91
 
    if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size)
 
90
    if (not pre_alloc || pre_alloc->size != size)
92
91
    {
93
 
      memory::internal::UsedMemory *mem, **prev= &mem_root->free;
 
92
      memory::internal::UsedMemory *mem, **prev= &this->free;
94
93
      /*
95
94
        Free unused blocks, so that consequent calls
96
95
        to reset_root_defaults won't eat away memory.
101
100
        if (mem->size == size)
102
101
        {
103
102
          /* We found a suitable block, no need to do anything else */
104
 
          mem_root->pre_alloc= mem;
 
103
          pre_alloc= mem;
105
104
          return;
106
105
        }
107
106
        if (mem->left + ALIGN_SIZE(sizeof(memory::internal::UsedMemory)) == mem->size)
108
107
        {
109
108
          /* remove block from the list and free it */
110
109
          *prev= mem->next;
111
 
          free(mem);
 
110
          std::free(mem);
112
111
        }
113
112
        else
114
113
          prev= &mem->next;
119
118
        mem->size= size;
120
119
        mem->left= pre_alloc_size;
121
120
        mem->next= *prev;
122
 
        *prev= mem_root->pre_alloc= mem;
 
121
        *prev= pre_alloc= mem;
123
122
      }
124
123
      else
125
124
      {
126
 
        mem_root->pre_alloc= 0;
 
125
        pre_alloc= 0;
127
126
      }
128
127
    }
129
128
  }
130
129
  else
131
130
  {
132
 
    mem_root->pre_alloc= 0;
 
131
    pre_alloc= 0;
133
132
  }
134
133
}
135
134
 
136
 
 
137
 
void *memory::alloc_root(memory::Root *mem_root, size_t length)
 
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)
138
150
{
139
 
  size_t get_size, block_size;
140
151
  unsigned char* point;
141
152
  memory::internal::UsedMemory *next= NULL;
142
153
  memory::internal::UsedMemory **prev;
143
 
  assert(alloc_root_inited(mem_root));
 
154
  assert(alloc_root_inited());
144
155
 
145
156
  length= ALIGN_SIZE(length);
146
 
  if ((*(prev= &mem_root->free)) != NULL)
 
157
  if ((*(prev= &this->free)) != NULL)
147
158
  {
148
159
    if ((*prev)->left < length &&
149
 
        mem_root->first_block_usage++ >= MAX_BLOCK_USAGE_BEFORE_DROP &&
 
160
        this->first_block_usage++ >= MAX_BLOCK_USAGE_BEFORE_DROP &&
150
161
        (*prev)->left < MAX_BLOCK_TO_DROP)
151
162
    {
152
163
      next= *prev;
153
164
      *prev= next->next;                        /* Remove block from list */
154
 
      next->next= mem_root->used;
155
 
      mem_root->used= next;
156
 
      mem_root->first_block_usage= 0;
 
165
      next->next= this->used;
 
166
      this->used= next;
 
167
      this->first_block_usage= 0;
157
168
    }
158
169
    for (next= *prev ; next && next->left < length ; next= next->next)
159
170
      prev= &next->next;
160
171
  }
161
172
  if (! next)
162
173
  {                                             /* Time to alloc new block */
163
 
    block_size= mem_root->block_size * (mem_root->block_num >> 2);
 
174
    size_t get_size, tmp_block_size;
 
175
 
 
176
    tmp_block_size= this->block_size * (this->block_num >> 2);
164
177
    get_size= length+ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
165
 
    get_size= max(get_size, block_size);
 
178
    get_size= max(get_size, tmp_block_size);
166
179
 
167
180
    if (!(next = static_cast<memory::internal::UsedMemory *>(malloc(get_size))))
168
181
    {
169
 
      if (mem_root->error_handler)
170
 
        (*mem_root->error_handler)();
 
182
      if (this->error_handler)
 
183
        (*this->error_handler)();
171
184
      return NULL;
172
185
    }
173
 
    mem_root->block_num++;
 
186
    this->block_num++;
174
187
    next->next= *prev;
175
188
    next->size= get_size;
176
189
    next->left= get_size-ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
178
191
  }
179
192
 
180
193
  point= (unsigned char*) ((char*) next+ (next->size-next->left));
181
 
  /*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
182
 
  if ((next->left-= length) < mem_root->min_malloc)
 
194
  /** @todo next part may be unneeded due to this->first_block_usage counter*/
 
195
  if ((next->left-= length) < this->min_malloc)
183
196
  {                                             /* Full block */
184
197
    *prev= next->next;                          /* Remove block from list */
185
 
    next->next= mem_root->used;
186
 
    mem_root->used= next;
187
 
    mem_root->first_block_usage= 0;
 
198
    next->next= this->used;
 
199
    this->used= next;
 
200
    this->first_block_usage= 0;
188
201
  }
189
 
  return((void*) point);
 
202
 
 
203
  return point;
190
204
}
191
205
 
192
206
 
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, ...)
 
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, ...)
213
230
{
214
231
  va_list args;
215
232
  char **ptr, *start, *res;
216
233
  size_t tot_length, length;
217
234
 
218
 
  va_start(args, root);
 
235
  (void)unused; // For some reason Sun Studio registers unused as not used.
 
236
  va_start(args, unused);
219
237
  tot_length= 0;
220
238
  while ((ptr= va_arg(args, char **)))
221
239
  {
224
242
  }
225
243
  va_end(args);
226
244
 
227
 
  if (!(start= (char*) memory::alloc_root(root, tot_length)))
 
245
  if (!(start= (char*) this->alloc_root(tot_length)))
228
246
    return(0);
229
247
 
230
 
  va_start(args, root);
 
248
  va_start(args, unused);
231
249
  res= start;
232
250
  while ((ptr= va_arg(args, char **)))
233
251
  {
241
259
 
242
260
#define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left)
243
261
 
244
 
/* Mark all data in blocks free for reusage */
245
 
 
246
 
static inline void mark_blocks_free(memory::Root* root)
 
262
/**
 
263
 * @brief
 
264
 * Mark all data in blocks free for reusage 
 
265
 */
 
266
void memory::Root::mark_blocks_free()
247
267
{
248
268
  memory::internal::UsedMemory *next;
249
269
  memory::internal::UsedMemory **last;
250
270
 
251
271
  /* iterate through (partially) free blocks, mark them free */
252
 
  last= &root->free;
253
 
  for (next= root->free; next; next= *(last= &next->next))
 
272
  last= &free;
 
273
  for (next= free; next; next= *(last= &next->next))
254
274
  {
255
275
    next->left= next->size - ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
256
276
    TRASH_MEM(next);
257
277
  }
258
278
 
259
279
  /* Combine the free and the used list */
260
 
  *last= next=root->used;
 
280
  *last= next= used;
261
281
 
262
282
  /* now go through the used blocks and mark them free */
263
283
  for (; next; next= next->next)
267
287
  }
268
288
 
269
289
  /* Now everything is set; Indicate that nothing is used anymore */
270
 
  root->used= 0;
271
 
  root->first_block_usage= 0;
 
290
  used= 0;
 
291
  first_block_usage= 0;
272
292
}
273
293
 
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)
 
294
/**
 
295
 * @brief
 
296
 * Deallocate everything used by memory::alloc_root or just move
 
297
 * used blocks to free list if called with MY_USED_TO_FREE
 
298
 *
 
299
 * @note
 
300
 * One can call this function either with root block initialised with
 
301
 * init_alloc_root() or with a zero:ed block.
 
302
 * It's also safe to call this multiple times with the same mem_root.
 
303
 *
 
304
 * @param   root     Memory root
 
305
 * @param   MyFlags  Flags for what should be freed:
 
306
 *   @li   MARK_BLOCKS_FREED    Don't free blocks, just mark them free
 
307
 *   @li   KEEP_PREALLOC        If this is not set, then free also the
 
308
 *                              preallocated block
 
309
 */
 
310
void memory::Root::free_root(myf MyFlags)
295
311
{
296
312
  memory::internal::UsedMemory *next,*old;
297
313
 
298
314
  if (MyFlags & memory::MARK_BLOCKS_FREE)
299
315
  {
300
 
    mark_blocks_free(root);
 
316
    this->mark_blocks_free();
301
317
    return;
302
318
  }
303
319
  if (!(MyFlags & memory::KEEP_PREALLOC))
304
 
    root->pre_alloc=0;
 
320
    this->pre_alloc=0;
305
321
 
306
 
  for (next=root->used; next ;)
 
322
  for (next=this->used; next ;)
307
323
  {
308
324
    old=next; next= next->next ;
309
 
    if (old != root->pre_alloc)
310
 
      free(old);
 
325
    if (old != this->pre_alloc)
 
326
      std::free(old);
311
327
  }
312
 
  for (next=root->free ; next ;)
 
328
  for (next=this->free ; next ;)
313
329
  {
314
330
    old=next; next= next->next;
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)
 
331
    if (old != this->pre_alloc)
 
332
      std::free(old);
 
333
  }
 
334
  this->used=this->free=0;
 
335
  if (this->pre_alloc)
 
336
  {
 
337
    this->free=this->pre_alloc;
 
338
    this->free->left=this->pre_alloc->size-ALIGN_SIZE(sizeof(memory::internal::UsedMemory));
 
339
    TRASH_MEM(this->pre_alloc);
 
340
    this->free->next=0;
 
341
  }
 
342
  this->block_num= 4;
 
343
  this->first_block_usage= 0;
 
344
}
 
345
 
 
346
/**
 
347
 * @brief
 
348
 * Duplicate a null-terminated string into memory allocated from within the
 
349
 * specified Root
 
350
 */
 
351
char *memory::Root::strdup_root(const char *str)
 
352
{
 
353
  return strmake_root(str, strlen(str));
 
354
}
 
355
 
 
356
/**
 
357
 * @brief
 
358
 * Copy the (not necessarily null-terminated) string into memory allocated
 
359
 * from within the specified Root
 
360
 *
 
361
 * @details
 
362
 * Note that the string is copied according to the length specified, so
 
363
 * null-termination is ignored. The duplicated string will be null-terminated,
 
364
 * even if the original string wasn't (one additional byte is allocated for
 
365
 * this purpose).
 
366
 */
 
367
char *memory::Root::strmake_root(const char *str, size_t len)
364
368
{
365
369
  char *pos;
366
 
  if ((pos=(char *)memory::alloc_root(root,len+1)))
 
370
  if ((pos= (char *)alloc_root(len+1)))
367
371
  {
368
372
    memcpy(pos,str,len);
369
373
    pos[len]=0;
371
375
  return pos;
372
376
}
373
377
 
374
 
 
375
 
void *memory::memdup_root(memory::Root *root, const void *str, size_t len)
 
378
/**
 
379
 * @brief
 
380
 * Duplicate the provided block into memory allocated from within the specified
 
381
 * Root
 
382
 *
 
383
 * @return
 
384
 * non-NULL pointer to a copy of the data if memory could be allocated, otherwise
 
385
 * NULL
 
386
 */
 
387
void *memory::Root::memdup_root(const void *str, size_t len)
376
388
{
377
389
  void *pos;
378
 
  if ((pos=memory::alloc_root(root,len)))
 
390
 
 
391
  if ((pos= this->alloc_root(len)))
379
392
    memcpy(pos,str,len);
 
393
 
380
394
  return pos;
381
395
}
382
396