~ubuntu-branches/ubuntu/karmic/firebird2.1/karmic

« back to all changes in this revision

Viewing changes to src/common/classes/alloc.h

  • Committer: Bazaar Package Importer
  • Author(s): Damyan Ivanov
  • Date: 2008-05-26 23:59:25 UTC
  • Revision ID: james.westby@ubuntu.com-20080526235925-2pnqj6nxpppoeaer
Tags: upstream-2.1.0.17798-0.ds2
ImportĀ upstreamĀ versionĀ 2.1.0.17798-0.ds2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      PROGRAM:        Client/Server Common Code
 
3
 *      MODULE:         alloc.h
 
4
 *      DESCRIPTION:    Memory Pool Manager (based on B+ tree)
 
5
 *
 
6
 *  The contents of this file are subject to the Initial
 
7
 *  Developer's Public License Version 1.0 (the "License");
 
8
 *  you may not use this file except in compliance with the
 
9
 *  License. You may obtain a copy of the License at
 
10
 *  http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
 
11
 *
 
12
 *  Software distributed under the License is distributed AS IS,
 
13
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied.
 
14
 *  See the License for the specific language governing rights
 
15
 *  and limitations under the License.
 
16
 *
 
17
 *  The Original Code was created by Nickolay Samofatov
 
18
 *  for the Firebird Open Source RDBMS project.
 
19
 *
 
20
 *  STL allocator is based on one by Mike Nordell and John Bellardo
 
21
 *
 
22
 *  Copyright (c) 2004 Nickolay Samofatov <nickolay@broadviewsoftware.com>
 
23
 *  and all contributors signed below.
 
24
 *
 
25
 *  All Rights Reserved.
 
26
 *
 
27
 *  Contributor(s):
 
28
 * 
 
29
 *              Alex Peshkoff <peshkoff@mail.ru>
 
30
 *                              added PermanentStorage and AutoStorage classes.
 
31
 *
 
32
 *
 
33
 */
 
34
 
 
35
#ifndef CLASSES_ALLOC_H
 
36
#define CLASSES_ALLOC_H
 
37
 
 
38
#include "firebird.h"
 
39
#include "fb_types.h"
 
40
 
 
41
#include <stdio.h>
 
42
#include "../jrd/common.h"
 
43
#include "../common/classes/fb_atomic.h"
 
44
#include "../common/classes/tree.h"
 
45
#include "../common/classes/locks.h"
 
46
#ifdef HAVE_STDLIB_H
 
47
#include <stdlib.h> /* XPG: prototypes for malloc/free have to be in
 
48
                                           stdlib.h (EKU) */
 
49
#endif
 
50
 
 
51
#ifdef _MSC_VER
 
52
#define THROW_BAD_ALLOC
 
53
#else
 
54
#define THROW_BAD_ALLOC throw (Firebird::BadAlloc)
 
55
#endif
 
56
 
 
57
#ifdef USE_VALGRIND
 
58
 
 
59
// Size of Valgrind red zone applied before and after memory block allocated for user
 
60
#define VALGRIND_REDZONE 8
 
61
 
 
62
// When memory block is deallocated by user from the pool it must pass queue of this 
 
63
// length before it is actually deallocated and access protection from it removed.
 
64
#define DELAYED_FREE_COUNT 1024
 
65
 
 
66
// When memory extent is deallocated when pool is destroying it must pass through
 
67
// queue of this length before it is actually returned to system
 
68
#define DELAYED_EXTENT_COUNT 32
 
69
 
 
70
#endif
 
71
 
 
72
namespace Firebird {
 
73
 
 
74
// Maximum number of B+ tree pages kept spare for tree allocation
 
75
// Since we store only unique fragment lengths in our tree there 
 
76
// shouldn't be more than 16K elements in it. This is why MAX_TREE_DEPTH 
 
77
// equal to 4 is more than enough
 
78
const int MAX_TREE_DEPTH = 4;
 
79
 
 
80
// Alignment for all memory blocks. Sizes of memory blocks in headers are measured in this units
 
81
const size_t ALLOC_ALIGNMENT = ALIGNMENT;
 
82
 
 
83
static inline size_t MEM_ALIGN(size_t value) {
 
84
        return FB_ALIGN(value, ALLOC_ALIGNMENT);
 
85
}
 
86
 
 
87
// Flags for memory block
 
88
const USHORT MBK_LARGE = 1; // Block is large, allocated from OS directly
 
89
const USHORT MBK_PARENT = 2; // Block is allocated from parent pool
 
90
const USHORT MBK_USED = 4; // Block is used
 
91
const USHORT MBK_LAST = 8; // Block is last in the extent
 
92
const USHORT MBK_DELAYED = 16; // Block is pending in the delayed free queue
 
93
 
 
94
struct FreeMemoryBlock {
 
95
        FreeMemoryBlock* fbk_next_fragment;
 
96
};
 
97
 
 
98
// Block header.
 
99
// Has size of 12 bytes for 32-bit targets and 16 bytes on 64-bit ones
 
100
struct MemoryBlock {
 
101
        USHORT mbk_flags;
 
102
        SSHORT mbk_type;
 
103
        union {
 
104
                struct {
 
105
                  // Length and offset are measured in bytes thus memory extent size is limited to 64k
 
106
                  // Larger extents are not needed now, but this may be icreased later via using allocation units 
 
107
                  USHORT mbk_length; // Actual block size: header not included, redirection list is included if applicable
 
108
                  USHORT mbk_prev_length;
 
109
                } mbk_small;
 
110
                // Measured in bytes
 
111
                ULONG mbk_large_length;
 
112
        };
 
113
#ifdef DEBUG_GDS_ALLOC
 
114
        const char* mbk_file;
 
115
        int mbk_line;
 
116
#endif
 
117
        union {
 
118
                class MemoryPool* mbk_pool;
 
119
                FreeMemoryBlock* mbk_prev_fragment;
 
120
        };
 
121
#if defined(USE_VALGRIND) && (VALGRIND_REDZONE != 0)
 
122
        const char mbk_valgrind_redzone[VALGRIND_REDZONE];
 
123
#endif
 
124
};
 
125
 
 
126
 
 
127
// This structure is appended to the end of block redirected to parent pool or operating system
 
128
// It is a doubly-linked list which we are going to use when our pool is going to be deleted
 
129
struct MemoryRedirectList {
 
130
        MemoryBlock* mrl_prev;
 
131
        MemoryBlock* mrl_next;
 
132
};
 
133
 
 
134
const SSHORT TYPE_POOL = -1;
 
135
const SSHORT TYPE_EXTENT = -2;
 
136
const SSHORT TYPE_LEAFPAGE = -3;
 
137
const SSHORT TYPE_TREEPAGE = -4;
 
138
 
 
139
// We store BlkInfo structures instead of BlkHeader pointers to get benefits from 
 
140
// processor cache-hit optimizations
 
141
struct BlockInfo {
 
142
        size_t bli_length;
 
143
        FreeMemoryBlock* bli_fragments;
 
144
        inline static const size_t& generate(const void* sender, const BlockInfo& i) {
 
145
                return i.bli_length;
 
146
        }
 
147
};
 
148
 
 
149
struct MemoryExtent {
 
150
        MemoryExtent *mxt_next;
 
151
        MemoryExtent *mxt_prev;
 
152
};
 
153
 
 
154
struct PendingFreeBlock {
 
155
        PendingFreeBlock *next;
 
156
};
 
157
 
 
158
class MemoryStats {
 
159
public:
 
160
        MemoryStats() : mst_usage(0), mst_mapped(0), mst_max_usage(0), mst_max_mapped(0) {}
 
161
        ~MemoryStats() {}
 
162
        size_t get_current_usage() const { return mst_usage.value(); }
 
163
        size_t get_maximum_usage() const { return mst_max_usage; }
 
164
        size_t get_current_mapping() const { return mst_mapped.value(); }
 
165
        size_t get_maximum_mapping() const { return mst_max_mapped; }
 
166
private:
 
167
        // Forbid copy constructor
 
168
        MemoryStats(const MemoryStats& object) {}
 
169
 
 
170
        // Currently allocated memory (without allocator overhead)
 
171
        // Useful for monitoring engine memory leaks
 
172
        AtomicCounter mst_usage;
 
173
        // Amount of memory mapped (including all overheads)
 
174
        // Useful for monitoring OS memory consumption
 
175
        AtomicCounter mst_mapped;
 
176
        
 
177
        // We don't particularily care about extreme precision of these max values,
 
178
        // this is why we don't synchronize them on Windows
 
179
        size_t mst_max_usage;
 
180
        size_t mst_max_mapped;
 
181
        
 
182
        friend class MemoryPool;        
 
183
};
 
184
 
 
185
 
 
186
// Memory pool based on B+ tree of free memory blocks
 
187
 
 
188
// We are going to have two target architectures:
 
189
// 1. Multi-process server with customizable lock manager
 
190
// 2. Multi-threaded server with single process (SUPERSERVER)
 
191
//
 
192
// MemoryPool inheritance looks weird because we cannot use
 
193
// any pointers to functions in shared memory. VMT usage in
 
194
// MemoryPool and its descendants is prohibited
 
195
class MemoryPool {
 
196
private:
 
197
        class InternalAllocator {
 
198
        public:
 
199
                void* allocate(size_t size) {
 
200
                        return ((MemoryPool*)this)->tree_alloc(size);
 
201
                }
 
202
                void deallocate(void* block) {
 
203
                        ((MemoryPool*)this)->tree_free(block);
 
204
                }
 
205
        };
 
206
        typedef BePlusTree<BlockInfo, size_t, InternalAllocator, BlockInfo> FreeBlocksTree;
 
207
        
 
208
        // We keep most of our structures uninitialized as long we redirect 
 
209
        // our allocations to parent pool
 
210
        bool parent_redirect;
 
211
 
 
212
        // B+ tree ordered by length 
 
213
        FreeBlocksTree freeBlocks;
 
214
 
 
215
        MemoryExtent *extents; // Linked list of all memory extents
 
216
 
 
217
        Vector<void*, 2> spareLeafs;
 
218
        Vector<void*, MAX_TREE_DEPTH + 1> spareNodes;
 
219
        bool needSpare;
 
220
        PendingFreeBlock *pendingFree;
 
221
 
 
222
    // Synchronization of this object is a little bit tricky. Allocations 
 
223
        // redirected to parent pool are not protected with our mutex and not 
 
224
        // accounted locally, i.e. redirect_amount and parent_redirected linked list
 
225
        // are synchronized with parent pool mutex only. All other pool members are 
 
226
        // synchronized with this mutex.
 
227
        Mutex lock;
 
228
 
 
229
        // Current usage counters for pool. Used to move pool to different statistics group
 
230
        AtomicCounter used_memory;
 
231
 
 
232
        size_t mapped_memory;
 
233
 
 
234
        MemoryPool *parent; // Parent pool. Used to redirect small allocations there
 
235
        MemoryBlock *parent_redirected, *os_redirected;
 
236
        size_t redirect_amount; // Amount of memory redirected to parent
 
237
                                                        // It is protected by parent pool mutex along with redirect list
 
238
        // Statistics group for the pool
 
239
        MemoryStats *stats;
 
240
        
 
241
#ifdef USE_VALGRIND
 
242
        // Circular FIFO buffer of read/write protected blocks pending free operation
 
243
        void* delayedFree[DELAYED_FREE_COUNT];
 
244
        int delayedFreeHandles[DELAYED_FREE_COUNT];
 
245
        size_t delayedFreeCount;
 
246
        size_t delayedFreePos;
 
247
#endif
 
248
 
 
249
        /* Returns NULL in case it cannot allocate requested chunk */
 
250
        static void* external_alloc(size_t &size);
 
251
 
 
252
        static void external_free(void* blk, size_t &size, bool pool_destroying);
 
253
        
 
254
        void* tree_alloc(size_t size);
 
255
 
 
256
        void tree_free(void* block);
 
257
 
 
258
        void updateSpare();
 
259
        
 
260
        inline void addFreeBlock(MemoryBlock* blk);
 
261
                
 
262
        void removeFreeBlock(MemoryBlock* blk);
 
263
        
 
264
        void free_blk_extent(MemoryBlock* blk);
 
265
        
 
266
        // Allocates small block from this pool. Pool must be locked during call
 
267
        void* internal_alloc(size_t size, SSHORT type = 0
 
268
#ifdef DEBUG_GDS_ALLOC
 
269
                , const char* file = NULL, int line = 0
 
270
#endif
 
271
        );
 
272
 
 
273
        // Deallocates small block from this pool. Pool must be locked during this call
 
274
        void internal_deallocate(void* block);
 
275
        
 
276
        // Forbid copy constructor, should never be called
 
277
        MemoryPool(const MemoryPool& pool) : freeBlocks((InternalAllocator*)this) { }
 
278
        
 
279
        // Used by pools to track memory usage. 
 
280
 
 
281
        // These 2 methods are thread-safe due to usage of atomic counters only
 
282
        inline void increment_usage(size_t size);
 
283
        inline void decrement_usage(size_t size);
 
284
 
 
285
        inline void increment_mapping(size_t size);
 
286
        inline void decrement_mapping(size_t size);
 
287
        
 
288
protected:
 
289
        // Do not allow to create and destroy pool directly from outside
 
290
        MemoryPool(MemoryPool* _parent, MemoryStats &_stats, void* first_extent, void* root_page);
 
291
 
 
292
        // This should never be called
 
293
        ~MemoryPool() {
 
294
        }
 
295
        
 
296
        // Used to create MemoryPool descendants
 
297
        static MemoryPool* internal_create(size_t instance_size, 
 
298
                MemoryPool* parent = NULL, MemoryStats& stats = *default_stats_group);
 
299
        
 
300
public:
 
301
        // Default statistics group for process
 
302
        static MemoryStats* default_stats_group;
 
303
 
 
304
        // Pool created for process
 
305
        static MemoryPool* processMemoryPool;
 
306
        
 
307
        // Create memory pool instance
 
308
        static MemoryPool* createPool(MemoryPool* parent = NULL, MemoryStats& stats = *default_stats_group) {
 
309
                return internal_create(sizeof(MemoryPool), parent, stats);
 
310
        }
 
311
        
 
312
        // Set context pool for current thread of execution
 
313
        static MemoryPool* setContextPool(MemoryPool* newPool);
 
314
        
 
315
        // Get context pool for current thread of execution
 
316
        static MemoryPool* getContextPool();
 
317
        
 
318
        // Set statistics group for pool. Usage counters will be decremented from 
 
319
        // previously set group and added to new
 
320
        void setStatsGroup(MemoryStats& stats);
 
321
 
 
322
        // Deallocate pool and all its contents
 
323
        static void deletePool(MemoryPool* pool);
 
324
 
 
325
        // Allocate memory block. Result is not zero-initialized.
 
326
        // It case of problems this method throws Firebird::BadAlloc
 
327
        void* allocate(size_t size, SSHORT type = 0
 
328
#ifdef DEBUG_GDS_ALLOC
 
329
                , const char* file = NULL, int line = 0
 
330
#endif
 
331
        );
 
332
 
 
333
        // Allocate memory block. In case of problems this method returns NULL
 
334
        void* allocate_nothrow(size_t size, SSHORT type = 0
 
335
#ifdef DEBUG_GDS_ALLOC
 
336
                , const char* file = NULL, int line = 0
 
337
#endif
 
338
        );
 
339
        
 
340
        void deallocate(void* block);
 
341
        
 
342
        // Check pool for internal consistent. When enabled, call is very expensive
 
343
        bool verify_pool(bool fast_checks_only = false);
 
344
 
 
345
        // Print out pool contents. This is debugging routine
 
346
        void print_contents(FILE*, bool = false, const char* filter_path = 0);
 
347
        
 
348
        // The same routine, but more easily callable from the debugger
 
349
        void print_contents(const char* filename, bool = false,
 
350
                const char* filter_path = 0);
 
351
        
 
352
        // Deallocate memory block. Pool is derived from block header
 
353
        static void globalFree(void* block) {
 
354
            if (block)
 
355
                  ((MemoryBlock*)((char*)block - MEM_ALIGN(sizeof(MemoryBlock))))->mbk_pool->deallocate(block);
 
356
        }
 
357
        
 
358
        // Allocate zero-initialized block of memory
 
359
        void* calloc(size_t size, SSHORT type = 0
 
360
#ifdef DEBUG_GDS_ALLOC
 
361
                , const char* file = NULL, int line = 0
 
362
#endif
 
363
        ) {
 
364
                void* result = allocate(size, type
 
365
#ifdef DEBUG_GDS_ALLOC
 
366
                        , file, line
 
367
#endif
 
368
                );
 
369
                memset(result, 0, size);
 
370
                return result;  
 
371
        }
 
372
 
 
373
        /// Returns the type associated with the allocated memory.
 
374
        static SSHORT blk_type(const void* mem) {
 
375
                return ((MemoryBlock*)((char *)mem - MEM_ALIGN(sizeof(MemoryBlock))))->mbk_type;
 
376
        }
 
377
        
 
378
        /// Returns the pool the memory was allocated from.
 
379
        //static MemoryPool* blk_pool(const void* mem) {
 
380
        //      return ((MemoryBlock*)((char *)mem - MEM_ALIGN(sizeof(MemoryBlock))))->mbk_pool;
 
381
        //}
 
382
        
 
383
        friend class InternalAllocator;
 
384
};
 
385
 
 
386
// Class intended to manage execution context pool stack
 
387
// Declare instance of this class when you need to set new context pool and it 
 
388
// will be restored automatically as soon holder variable gets out of scope
 
389
class ContextPoolHolder {
 
390
public:
 
391
        ContextPoolHolder(MemoryPool* newPool) {
 
392
                savedPool = MemoryPool::setContextPool(newPool);
 
393
        }
 
394
        ~ContextPoolHolder() {
 
395
                MemoryPool::setContextPool(savedPool);
 
396
        }
 
397
private:
 
398
        MemoryPool* savedPool;  
 
399
};
 
400
 
 
401
// template enabling common use of old and new pools control code
 
402
// to be dropped when old-style code goes away
 
403
template <typename SubsystemThreadData, typename SubsystemPool>
 
404
class SubsystemContextPoolHolder
 
405
: public ContextPoolHolder
 
406
{
 
407
public:
 
408
        SubsystemContextPoolHolder <SubsystemThreadData, SubsystemPool> 
 
409
        (
 
410
                SubsystemThreadData* subThreadData, 
 
411
                SubsystemPool* newPool
 
412
        ) 
 
413
                : ContextPoolHolder(newPool), 
 
414
                savedThreadData(subThreadData),
 
415
                savedPool(savedThreadData->getDefaultPool()) 
 
416
        {
 
417
                savedThreadData->setDefaultPool(newPool);
 
418
        }
 
419
        ~SubsystemContextPoolHolder() {
 
420
                savedThreadData->setDefaultPool(savedPool);
 
421
        }
 
422
private:
 
423
        SubsystemThreadData* savedThreadData;
 
424
        SubsystemPool* savedPool;
 
425
};
 
426
 
 
427
} // namespace Firebird
 
428
 
 
429
using Firebird::MemoryPool;
 
430
 
 
431
inline static MemoryPool* getDefaultMemoryPool() { return Firebird::MemoryPool::processMemoryPool; }
 
432
 
 
433
// Global versions of operators new and delete
 
434
inline void* operator new(size_t s) THROW_BAD_ALLOC
 
435
{
 
436
        return getDefaultMemoryPool()->allocate(s, 0
 
437
#ifdef DEBUG_GDS_ALLOC
 
438
          ,__FILE__, __LINE__
 
439
#endif
 
440
        );
 
441
}
 
442
inline void* operator new[](size_t s) THROW_BAD_ALLOC
 
443
{
 
444
        return getDefaultMemoryPool()->allocate(s, 0
 
445
#ifdef DEBUG_GDS_ALLOC
 
446
          ,__FILE__, __LINE__
 
447
#endif
 
448
        );
 
449
}
 
450
 
 
451
inline void* operator new(size_t, void* ptr) throw() 
 
452
{
 
453
        return ptr;
 
454
}
 
455
inline void* operator new[](size_t, void* ptr) throw() 
 
456
{
 
457
        return ptr;
 
458
}
 
459
 
 
460
inline void operator delete(void* mem) throw()
 
461
{
 
462
        Firebird::MemoryPool::globalFree(mem);
 
463
}
 
464
inline void operator delete[](void* mem) throw()
 
465
{
 
466
        Firebird::MemoryPool::globalFree(mem);
 
467
}
 
468
 
 
469
#ifdef DEBUG_GDS_ALLOC
 
470
inline void* operator new(size_t s, Firebird::MemoryPool& pool, const char* file, int line) {
 
471
        return pool.allocate(s, 0, file, line);
 
472
}
 
473
inline void* operator new[](size_t s, Firebird::MemoryPool& pool, const char* file, int line) {
 
474
        return pool.allocate(s, 0, file, line);
 
475
}
 
476
#define FB_NEW(pool) new(pool, __FILE__, __LINE__)
 
477
#define FB_NEW_RPT(pool, count) new(pool, count, __FILE__, __LINE__)
 
478
#else
 
479
inline void* operator new(size_t s, Firebird::MemoryPool& pool) {
 
480
        return pool.allocate(s);
 
481
}
 
482
inline void* operator new[](size_t s, Firebird::MemoryPool& pool) {
 
483
        return pool.allocate(s);
 
484
}
 
485
#define FB_NEW(pool) new(pool)
 
486
#define FB_NEW_RPT(pool, count) new(pool, count)
 
487
#endif
 
488
 
 
489
 
 
490
namespace Firebird
 
491
{
 
492
        // Permanent storage is used as base class for all objects,
 
493
        // performing memory allocation in methods other than 
 
494
        // constructors of this objects. Permanent means that pool,
 
495
        // which will be later used for such allocations, must
 
496
        // be explicitly passed in all constructors of such object.
 
497
        class PermanentStorage {
 
498
        private:
 
499
                MemoryPool& pool;
 
500
        protected:
 
501
                explicit PermanentStorage(MemoryPool& p) : pool(p) { }
 
502
                MemoryPool& getPool() const { return pool; }
 
503
        };
 
504
 
 
505
        // Automatic storage is used as base class for objects,
 
506
        // that may have constructors without explicit MemoryPool
 
507
        // parameter. In this case AutoStorage sends AutoMemoryPool
 
508
        // to PermanentStorage. To ensure this operation to be safe
 
509
        // such trick possible only for local (on stack) variables.
 
510
        class AutoStorage : public PermanentStorage {
 
511
        private:
 
512
#if defined(DEV_BUILD)
 
513
                void ProbeStack() const;
 
514
#endif
 
515
        public:
 
516
                static MemoryPool& getAutoMemoryPool();
 
517
        protected:
 
518
                AutoStorage() : PermanentStorage(getAutoMemoryPool()) {
 
519
#if defined(DEV_BUILD)
 
520
                        ProbeStack();
 
521
#endif
 
522
                }
 
523
                explicit AutoStorage(MemoryPool& p) : PermanentStorage(p) { }
 
524
        };
 
525
        
 
526
} // namespace Firebird
 
527
 
 
528
 
 
529
#endif // CLASSES_ALLOC_H