cfad47cfa3/tads3/vmpool.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* 
2
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
3
 *   
4
 *   Please see the accompanying license file, LICENSE.TXT, for information
5
 *   on using and copying this software.  
6
 */
7
/*
8
Name
9
  vmpool.h - VM constant pool manager
10
Function
11
  
12
Notes
13
  
14
Modified
15
  10/20/98 MJRoberts  - Creation
16
*/
17
18
#ifndef VMPOOL_H
19
#define VMPOOL_H
20
21
#include <stdlib.h>
22
#include <memory.h>
23
24
#include "vmtype.h"
25
26
/* include the pool selection mechanism */
27
#include "vmpoolsl.h"
28
29
/* ------------------------------------------------------------------------ */
30
/*
31
 *   Constant pool page information.  This structure tracks memory for one
32
 *   page.  
33
 */
34
struct CVmPool_pg
35
{
36
    /* memory containing the data in this page */
37
    const char *mem;
38
};
39
40
   
41
/* ------------------------------------------------------------------------ */
42
/*
43
 *   Constant Pool Backing Store Interface.  This is an abstract interface
44
 *   that pool clients must implement to provide the pool with the means
45
 *   of loading pages.
46
 *   
47
 *   Note that the backing store, like the pool itself, is considered
48
 *   read-only by the pool manager.  The pool manager never needs to write
49
 *   data to the backing store, and expects the backing store to remain
50
 *   constant throughout the existence of the pool (hence the pool never
51
 *   needs to reload any data from the backing store that it already has
52
 *   in cache).  
53
 */
54
class CVmPoolBackingStore
55
{
56
public:
57
    /* 
58
     *   since this class is abstract, make sure all subclasses have virtual
59
     *   destructors 
60
     */
61
    virtual ~CVmPoolBackingStore() { }
62
    
63
    /*
64
     *   Determine the total number of pages that are available to be
65
     *   loaded.  Implementations of the pool manager that pre-load all
66
     *   pages use this function to determine how many pages are available
67
     *   for loading.  
68
     */
69
    virtual size_t vmpbs_get_page_count() = 0;
70
71
    /*
72
     *   Get the common page size in the underying store.  Individual
73
     *   pages may not use the entire page size, but no page may be larger
74
     *   than the common size.  
75
     */
76
    virtual size_t vmpbs_get_common_page_size() = 0;
77
78
    /*
79
     *   Given a starting offset and a page size, calculate how much space is
80
     *   actually needed for the page at the offset.  This is provided to
81
     *   allow for partial pages, which don't need the full page size
82
     *   allocated.  Simple implementations can simply always return the full
83
     *   page size.  
84
     */
85
    virtual size_t vmpbs_get_page_size(pool_ofs_t ofs, size_t page_size) = 0;
86
87
    /*
88
     *   Given a starting offset, allocate space for the given page and
89
     *   load it into memory.  page_size is the normal page size in bytes,
90
     *   and load_size is the actual number of bytes to be allocated and
91
     *   loaded (this will be the value previously returned by
92
     *   vmpbs_get_page_size for the page).
93
     *   
94
     *   This should throw an exception if an error occurs.  
95
     */
96
    virtual const char
97
        *vmpbs_alloc_and_load_page(pool_ofs_t ofs, size_t page_size,
98
                                   size_t load_size) = 0;
99
100
    /*
101
     *   Delete memory allocated by vmpbs_alloc_and_load_page().  The pool
102
     *   will call this for each page previously allocated.  'page_size'
103
     *   is the normal page size in bytes for the entire pool.  
104
     */
105
    virtual void vmpbs_free_page(const char *mem, pool_ofs_t ofs,
106
                                 size_t page_size) = 0;
107
108
    /*
109
     *   Given a starting offset, load the page into the given memory,
110
     *   which is allocated and managed by the caller.  page_size is the
111
     *   normal page size in bytes, and load_size is the actual number of
112
     *   bytes to be loaded (this will be the value previously returned by
113
     *   vmpbs_get_page_size for the page).
114
     *   
115
     *   This should throw an exception if an error occurs.  
116
     */
117
    virtual void vmpbs_load_page(pool_ofs_t ofs, size_t page_size,
118
                                 size_t load_size, char *mem) = 0;
119
120
    /*
121
     *   Determine if my pages are writable.  Returns true if so, false if
122
     *   not.  If the pool pages are directly mapped to the underlying
123
     *   data file, this should return false.  For example, an
124
     *   implementation for a palm-top computer without an external
125
     *   storage device might simply store the image file directly in
126
     *   memory, and the backing store would map directly onto this memory
127
     *   so that the original copy of the image file in memory can be used
128
     *   as the loaded version as well.  In such cases, the backing store
129
     *   should certainly not be writable.  For an implementation that
130
     *   copies data from an external storage device (typically a hard
131
     *   disk), writing to the backing store copy would cause no change to
132
     *   the original image file data, hence this can return true in such
133
     *   cases.  
134
     */
135
    virtual int vmpbs_is_writable() { return FALSE; }
136
};
137
138
139
/* ------------------------------------------------------------------------ */
140
/*
141
 *   Base constant memory pool class 
142
 */
143
class CVmPool
144
{
145
public:
146
    CVmPool() { }
147
    virtual ~CVmPool() { }
148
149
    /*
150
     *   Get the dynamic data manager for the pool.  If the pool supports
151
     *   dynamic data, it should return a non-null pointer.  If the pool
152
     *   doesn't support dynamic data, it should return null.  
153
     */
154
    virtual class CVmPoolDynamic *get_dynamic_ifc() { return 0; }
155
156
    /* 
157
     *   Attach to the given backing store to provide the the page data.  
158
     */
159
    virtual void
160
        attach_backing_store(class CVmPoolBackingStore *backing_store) = 0;
161
162
    /* 
163
     *   Detach from backing store - this must be called before the backing
164
     *   store object can be deleted.  
165
     */
166
    virtual void detach_backing_store() { backing_store_ = 0; }
167
168
    /* 
169
     *   Translate an address from a pool offset to a physical location.
170
     *   Note that translating an address may invalidate a previously
171
     *   translated address in a swapping implementation of the pool manager,
172
     *   so callers should take care to assume only one translated address in
173
     *   a given pool is valid at a time.
174
     *   
175
     *   Because this routine is called extremely frequently, we don't make
176
     *   it a virtual.  Instead, we depend upon the final subclass to define
177
     *   the method as a non-virtual, so that it can be in-lined.  This means
178
     *   that pool object references must all be declared with the final
179
     *   subclass.  
180
     */
181
    /* virtual const char *get_ptr(pool_ofs_t ofs) = 0; */
182
183
    /*
184
     *   Translate an address from a pool offset to a physical location, and
185
     *   return a writable pointer to the location.  This will fail for any
186
     *   type of pool implementation that doesn't support writable memory
187
     *   pointers (for example, the swapping pool doesn't allow write
188
     *   pointers, because it doesn't keep track of dirty pages when
189
     *   performing swapping).
190
     *   
191
     *   As with get_ptr(), this isn't actually virtual, but is instead to be
192
     *   defined in each final subclass.  
193
     */
194
    /* virtual char *get_writable_ptr(pool_ofs_t ofs) = 0; */
195
196
    /* 
197
     *   Get the page size.  This reflects the size of the pages in the
198
     *   backing store (usually the image file); this doesn't necessarily
199
     *   indicate anything about the way we manage the pool memory. 
200
     */
201
    size_t get_page_size() const { return page_size_; }
202
203
    /* get the number of pages in the pool */
204
    size_t get_page_count() const;
205
206
protected:
207
    /* 
208
     *   page size in bytes - this is simply the number of bytes on each page
209
     *   (each page in the pool has the same number of bytes) 
210
     */
211
    size_t page_size_;
212
213
    /* backing store */
214
    class CVmPoolBackingStore *backing_store_;
215
216
    /* our single contiguous allocation block */
217
    char *mem_;
218
};
219
220
/* ------------------------------------------------------------------------ */
221
/*
222
 *   "Flat" memory pool.  This type of pool loads all pages into a single
223
 *   contiguous chunk of memory.  This is suitable for platforms with large
224
 *   linear memory spaces, such as 32-bit platforms.
225
 *   
226
 *   This type of pool does not support dynamic allocation, so it's not
227
 *   suitable for use in a debugger build or other configurations that
228
 *   require dynamic allocation of pool space.  
229
 */
230
class CVmPoolFlat: public CVmPool
231
{
232
public:
233
    CVmPoolFlat()
234
    {
235
        /* we don't have our memory chunk yet */
236
        mem_ = 0;
237
    }
238
    ~CVmPoolFlat();
239
240
    /* terminate - we don't need to do anything here */
241
    void terminate() { }
242
243
    /* attach to the backing store - loads all pages */
244
    void attach_backing_store(class CVmPoolBackingStore *backing_store);
245
246
    /* detach from the backing store */
247
    void detach_backing_store();
248
249
    /* 
250
     *   Translate an address.  Since all of our memory is in one large
251
     *   contiguous chunk, this is extremely simple: just return the base of
252
     *   our memory block, offset by the pool offset.  
253
     */
254
    inline const char *get_ptr(pool_ofs_t ofs) { return mem_ + ofs; }
255
256
    /* we do not support writable pointers */
257
    inline char *get_writable_ptr(pool_ofs_t ofs) { return 0; }
258
};
259
260
/* ------------------------------------------------------------------------ */
261
/*
262
 *   Paged constant pool.
263
 *   
264
 *   This type of pool is divided into pages.  A given object must be
265
 *   entirely contained in a single page.
266
 *   
267
 *   Each object is referenced by its address in the constant pool.  An
268
 *   object address is simply an offset into the pool.  
269
 */
270
class CVmPoolPaged: public CVmPool
271
{
272
public:
273
    /* create the pool */
274
    CVmPoolPaged()
275
    {
276
        /* no page slots allocated yet */
277
        pages_ = 0;
278
        page_slots_ = 0;
279
        page_slots_max_ = 0;
280
281
        /* we don't have a backing store yet */
282
        backing_store_ = 0;
283
284
        /* we don't know the page size yet */
285
        page_size_ = 0;
286
        log2_page_size_ = 0;
287
    }
288
289
    /* 
290
     *   Delete the pool.  Call our non-virtual termination routine, as we
291
     *   can't use virtuals in destructors (not in the normal fashion,
292
     *   anyway).  
293
     */
294
    virtual ~CVmPoolPaged() { terminate_nv(); }
295
296
    /* delete everything in the pool using our base terminator routine */
297
    virtual void terminate() { terminate_nv(); }
298
299
    /* 
300
     *   Attach to the given backing store to provide the the page data.  
301
     */
302
    virtual void
303
        attach_backing_store(class CVmPoolBackingStore *backing_store);
304
305
protected:
306
    /* non-virtual termination routine */
307
    void terminate_nv()
308
    {
309
        /* free our page memory */
310
        delete_page_list();
311
    }
312
    
313
    /* delete our page list, if any */
314
    void delete_page_list();
315
316
    /* allocate or expand the page slot list */
317
    void alloc_page_slots(size_t slots);
318
    
319
    /* 
320
     *   Calculate which page we need, and the offset within the page, for
321
     *   a given pool offset.  The page is the offset divided by the page
322
     *   size; since the page size is a power of two, this is simply a bit
323
     *   shift by log2(page_size).  The page offset is the remainder after
324
     *   dividing the offset by the page size; again because the page size
325
     *   is a power of two, we can calculate this remainder simply by
326
     *   AND'ing the offset with the page size minus one.  (Using these
327
     *   shift and mask operations may seem a little obscure, but it's so
328
     *   much faster on most machines than integer division that we're
329
     *   willing to be a little obscure in exchange for the performance
330
     *   gain.)  
331
     */
332
    inline size_t get_page_for_ofs(pool_ofs_t ofs) const
333
    {
334
        return (size_t)(ofs >> log2_page_size_);
335
    }
336
337
    inline size_t get_ofs_for_ofs(pool_ofs_t ofs) const
338
    {
339
        return (size_t)(ofs & (page_size_ - 1));
340
    }
341
342
    /* get the starting offset on the given page */
343
    pool_ofs_t get_page_start_ofs(size_t pg) const
344
    {
345
        return ((pool_ofs_t)pg) << log2_page_size_;
346
    }
347
348
    /*
349
     *   The page list.  This is an array of CVmPool_pg structures; each
350
     *   structure keeps track of one page in the pool. 
351
     *   
352
     *   The page identified by the first page information structure contains
353
     *   pool offsets 0 through (page_size - 1); the next contains offsets
354
     *   page_size through (2*page_size - 1); and so on.  
355
     */
356
    CVmPool_pg *pages_;
357
358
    /* 
359
     *   The number of page slots in the page list.  This starts at the
360
     *   initial page size and can grow dynamically as more pages are added.
361
     */
362
    size_t page_slots_;
363
364
    /* 
365
     *   The maximum of allocated pages_ array entries.  This might be larger
366
     *   than page_slots_, because we sometimes allocate more slots than we
367
     *   currently need to avoid having to allocate on every new page
368
     *   addition.  
369
     */
370
    size_t page_slots_max_;
371
372
    /* log2 of the page size */
373
    int log2_page_size_;
374
};
375
376
377
/* ------------------------------------------------------------------------ */
378
/*
379
 *   Two-tiered paged pool.  This is similar to the paged pool
380
 *   implementation, but uses a two-level page table: the first-level page
381
 *   table containers pointers to the second-level tables, and the
382
 *   second-level tables contain the pointers to the actual pages.
383
 *   
384
 *   This class is not currently used, because the two-level scheme isn't
385
 *   required in practice for modern machines and is less efficient than the
386
 *   single-level page table implemented in CVmPoolPaged.  We retain this
387
 *   two-level code in case it's ever needed, though, because the two-level
388
 *   scheme might be useful for 16-bit segmented architectures.
389
 *   
390
 *   The advantage of the two-level scheme is that it allows very large
391
 *   memory spaces to be addressable without any single large allocations;
392
 *   the single-tier paged pool requires a single allocation equal to the
393
 *   total aggregate memory size divided by the page size times the size of a
394
 *   page pointer, which could be a fairly large single allocation for an
395
 *   extremely large aggregate pool size.  However, it doesn't currently
396
 *   appear that the single-tier paging scheme will impose any limits that
397
 *   will be encountered in actual practice.  
398
 */
399
#if 0
400
401
/* number of page information structures in each subarray */
402
const size_t VMPOOL_SUBARRAY_SIZE = 4096;
403
404
class CVmPoolPaged2
405
{
406
public:
407
    /* create the pool */
408
    CVmPoolPaged2()
409
    {
410
        /* no page slots allocated yet */
411
        pages_ = 0;
412
        page_slots_ = 0;
413
414
        /* we don't have a backing store yet */
415
        backing_store_ = 0;
416
417
        /* we don't know the page size yet */
418
        page_size_ = 0;
419
        log2_page_size_ = 0;
420
    }
421
422
    /* delete the pool */
423
    virtual ~CVmPoolPaged2();
424
425
    /* 
426
     *   Attach to the given backing store to provide the the page data.  
427
     */
428
    virtual void
429
        attach_backing_store(class CVmPoolBackingStore *backing_store);
430
431
protected:
432
    /* delete our page list, if any */
433
    void delete_page_list();
434
435
    /* allocate or expand the page slot list */
436
    void alloc_page_slots(size_t slots);
437
438
    /* 
439
     *   Calculate which page we need, and the offset within the page, for a
440
     *   given pool offset.  The page is the offset divided by the page size;
441
     *   since the page size is a power of two, this is simply a bit shift by
442
     *   log2(page_size).  The page offset is the remainder after dividing
443
     *   the offset by the page size; again because the page size is a power
444
     *   of two, we can calculate this remainder simply by AND'ing the offset
445
     *   with the page size minus one.  (Using these shift and mask
446
     *   operations may seem a little obscure, but it's so much faster on
447
     *   most machines than integer division that we're willing to be a
448
     *   little obscure in exchange for the performance gain.)  
449
     */
450
    inline size_t get_page_for_ofs(pool_ofs_t ofs) const
451
    {
452
        return (size_t)(ofs >> log2_page_size_);
453
    }
454
455
    inline size_t get_ofs_for_ofs(pool_ofs_t ofs) const
456
    {
457
        return (size_t)(ofs & (page_size_ - 1));
458
    }
459
460
    /* get the starting offset on the given page */
461
    pool_ofs_t get_page_start_ofs(size_t pg) const
462
    {
463
        return ((pool_ofs_t)pg) << log2_page_size_;
464
    }
465
466
    /* get the number of subarrays */
467
    size_t get_subarray_count() const
468
        { return ((page_slots_ + VMPOOL_SUBARRAY_SIZE - 1)
469
                  / VMPOOL_SUBARRAY_SIZE); }
470
471
    /* get the page information structure for a given index */
472
    inline CVmPool_pg *get_page_info(size_t idx) const
473
        { return &(pages_[idx / VMPOOL_SUBARRAY_SIZE]
474
                   [idx % VMPOOL_SUBARRAY_SIZE]); }
475
476
    /*
477
     *   The page list.  Each entry in this array is a subarray containing
478
     *   VMPOOL_SUBARRAY_SIZE page information structures, each of contains
479
     *   information on a page.  Conceptually, the two-tiered array forms a
480
     *   single array; we keep two levels of arrays to accommodate 16-bit
481
     *   machines where a single large could be too large for a single 64k
482
     *   segment.
483
     *   
484
     *   The page identified by the first page information structure contains
485
     *   pool offsets 0 through (page_size - 1); the next contains offsets
486
     *   page_size through (2*page_size - 1); and so on.  
487
     */
488
    CVmPool_pg **pages_;
489
490
    /* 
491
     *   The number of slots allocated in the page list.  This starts at
492
     *   the initial page size and can grow dynamically as more pages are
493
     *   added. 
494
     */
495
    size_t page_slots_;
496
497
    /* log2 of the page size */
498
    int log2_page_size_;
499
};
500
#endif /* 0 */
501
502
/* ------------------------------------------------------------------------ */
503
/*
504
 *   Dynamic pool manager interface.  This is an abstract interface that
505
 *   provides a way to create new pool objects dynamically, and later
506
 *   delete those objects.
507
 *   
508
 *   Some types of pools support this interface, others do not.  
509
 */
510
511
/*
512
 *   Dynamic pool object handle.  Each object in a dynamic pool is
513
 *   identified by an object handle.  When the dynpool_compress() method
514
 *   is called in the dynamic pool interface, the pool addresses of
515
 *   objects can change.  
516
 */
517
class CVmPoolDynObj
518
{
519
public:
520
    CVmPoolDynObj(pool_ofs_t ofs, size_t len)
521
    {
522
        /* remember my location and size */
523
        ofs_ = ofs;
524
        len_ = len;
525
526
        /* not in a list yet */
527
        nxt_ = prv_ = 0;
528
529
        /* presume it's in use */
530
        is_free_ = FALSE;
531
    }
532
    
533
    /* get/set the current pool address of this object */
534
    pool_ofs_t get_ofs() const { return ofs_; }
535
    void set_ofs(pool_ofs_t ofs) { ofs_ = ofs; }
536
537
    /* get/set my length */
538
    size_t get_len() const { return len_; }
539
    void set_len(size_t len) { len_ = len; }
540
541
    /* get/set the next object in the list */
542
    CVmPoolDynObj *get_next() const { return nxt_; }
543
    void set_next(CVmPoolDynObj *obj) { nxt_ = obj; }
544
545
    /* get/set the previous object in the list */
546
    CVmPoolDynObj *get_prev() const { return prv_; }
547
    void set_prev(CVmPoolDynObj *obj) { prv_ = obj; }
548
549
    /* get/set the 'free' flag */
550
    int is_free() const { return is_free_; }
551
    void set_free(int f) { is_free_ = f; }
552
553
private:
554
    /* my pool address */
555
    pool_ofs_t ofs_;
556
557
    /* my length */
558
    size_t len_;
559
560
    /* next/previous dynamic object in the list */
561
    CVmPoolDynObj *nxt_;
562
    CVmPoolDynObj *prv_;
563
564
    /* flag: this object's pool space is free */
565
    uint is_free_ : 1;
566
};
567
568
/*
569
 *   dynamic pool manager interface 
570
 */
571
class CVmPoolDynamic
572
{
573
public:
574
    /* 
575
     *   Allocate a new object in the pool.  Returns a non-null object
576
     *   handle on success, or zero on failure.  This can fail for
577
     *   different reasons: the object might be too large to fit in a
578
     *   single pool page, or there might be insufficient physical memory
579
     *   available.  
580
     */
581
    virtual CVmPoolDynObj *dynpool_alloc(size_t len) = 0;
582
583
    /*
584
     *   Delete an object in the pool. 
585
     */
586
    virtual void dynpool_delete(CVmPoolDynObj *obj) = 0;
587
588
    /*
589
     *   Compress the pool.  To the extent possible, rearranges the
590
     *   dynamic objects in the pool to remove space left vacant by
591
     *   deleted objects.  When this is called, the addresses of pool
592
     *   objects can change; this is the only time that addresses can
593
     *   change.  This should be called after each batch of deletions to
594
     *   ensure that pool space is not wasted.  (Deleting an object
595
     *   doesn't automatically compress the pool, so that a single
596
     *   compression pass can be made after a batch of deletions.)  
597
     */
598
    virtual void dynpool_compress() = 0;
599
};
600
601
/* ------------------------------------------------------------------------ */
602
/*
603
 *   In-memory pool implementation.  This pool implementation pre-loads
604
 *   all available pages in the pool and keeps the complete pool in memory
605
 *   at all times.  
606
 */
607
class CVmPoolInMem: public CVmPoolPaged, public CVmPoolDynamic
608
{
609
public:
610
    CVmPoolInMem()
611
    {
612
        /* no pages yet -> no dynamic pages */
613
        first_dyn_page_ = 0;
614
615
        /* no dynamic objects yet */
616
        dyn_head_ = dyn_tail_ = 0;
617
    }
618
619
    /* 
620
     *   delete - call our non-virtual terminator (use the non-virtual
621
     *   version, as this will just do our local termination; since we'll
622
     *   implicitly inherit the base class destructor, we don't want to
623
     *   explicitly inherit its termination as well) 
624
     */
625
    ~CVmPoolInMem() { terminate_nv(); }
626
627
    /* terminate */
628
    void terminate()
629
    {
630
        /* call our own non-virtual termination routine */
631
        terminate_nv();
632
633
        /* inherit our base class handling */
634
        CVmPoolPaged::terminate();
635
    }
636
637
    /* I provide a dynamic pool interface */
638
    virtual class CVmPoolDynamic *get_dynamic_ifc() { return this; }
639
640
    /* attach to the backing store - loads all pages */
641
    void attach_backing_store(class CVmPoolBackingStore *backing_store);
642
643
    /* detach from the backing store */
644
    void detach_backing_store();
645
646
    /* 
647
     *   translate an address - since the pool is always in memory, we can
648
     *   translate an address simply by doing the arithmetic and finding
649
     *   the needed page, which is always loaded 
650
     */
651
    inline const char *get_ptr(pool_ofs_t ofs)
652
    {
653
        /* translate the address through our page array */
654
        return (pages_[get_page_for_ofs(ofs)].mem + get_ofs_for_ofs(ofs));
655
    }
656
657
    /* 
658
     *   get a writable pointer - we allow write pointers as long as the
659
     *   backing store does 
660
     */
661
    inline char *get_writable_ptr(pool_ofs_t ofs)
662
    {
663
        /* 
664
         *   If the backing store allows writing to its pages, allow
665
         *   writing.  In any case, if the offset is in a dynamic page, we
666
         *   can always write to it, regardless of backing store policy,
667
         *   because we allocate and control the dynamic pages ourselves.  
668
         */
669
        if (backing_store_->vmpbs_is_writable()
670
            || get_page_for_ofs(ofs) >= first_dyn_page_)
671
        {
672
            /* 
673
             *   the backing store memory is writable - return a writable
674
             *   version of the normal pointer to this memory 
675
             */
676
            return (char *)get_ptr(ofs);
677
        }
678
        else
679
        {
680
            /* the backing store memory is non-writable - return failure */
681
            return 0;
682
        }
683
    }
684
685
    /* 
686
     *   dynamic pool interface 
687
     */
688
689
    /* allocate a dynamic object */
690
    virtual CVmPoolDynObj *dynpool_alloc(size_t len);
691
692
    /* delete a dyanmic object */
693
    virtual void dynpool_delete(CVmPoolDynObj *obj);
694
695
    /* compress the dynamic portion of the pool */
696
    virtual void dynpool_compress();
697
698
private:
699
    /* non-virtual termination */
700
    void terminate_nv();
701
702
    /* free any pages we allocated from the backing store */
703
    void free_backing_pages();
704
705
    /* add a dynamic handle at the end of the list */
706
    void append_dyn(CVmPoolDynObj *obj);
707
708
    /* insert a dynamic handle after the given dynamic handle */
709
    void insert_dyn(CVmPoolDynObj *obj, CVmPoolDynObj *new_obj);
710
711
    /* unlink a dynamic handle from the list */
712
    void unlink_dyn(CVmPoolDynObj *obj);
713
714
    /* 
715
     *   First dynamically-allocated page index - all pages from this page
716
     *   to the last page are dynamic.  If this page index is equal to the
717
     *   total number of pages, there are no dynamic pages, since this
718
     *   index refers to an invalid page.  
719
     */
720
    size_t first_dyn_page_;
721
722
    /* head and tail of list of dynamic pool objects */
723
    CVmPoolDynObj *dyn_head_;
724
    CVmPoolDynObj *dyn_tail_;
725
};
726
727
#endif /* VMPOOL_H */
728