mem_block.h

00001 /* -*- mode:C++; c-basic-offset:4 -*-
00002      Shore-MT -- Multi-threaded port of the SHORE storage manager
00003    
00004                        Copyright (c) 2007-2009
00005       Data Intensive Applications and Systems Labaratory (DIAS)
00006                Ecole Polytechnique Federale de Lausanne
00007    
00008                          All Rights Reserved.
00009    
00010    Permission to use, copy, modify and distribute this software and
00011    its documentation is hereby granted, provided that both the
00012    copyright notice and this permission notice appear in all copies of
00013    the software, derivative works or modified versions, and any
00014    portions thereof, and that both notices appear in supporting
00015    documentation.
00016    
00017    This code is distributed in the hope that it will be useful, but
00018    WITHOUT ANY WARRANTY; without even the implied warranty of
00019    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS
00020    DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
00021    RESULTING FROM THE USE OF THIS SOFTWARE.
00022 */
00023 /*<std-header orig-src='shore' incl-file-exclusion='MEM_BLOCK_H'>
00024 
00025  $Id: mem_block.h,v 1.2 2010/06/23 23:42:57 nhall Exp $
00026 
00027 SHORE -- Scalable Heterogeneous Object REpository
00028 
00029 Copyright (c) 1994-99 Computer Sciences Department, University of
00030                       Wisconsin -- Madison
00031 All Rights Reserved.
00032 
00033 Permission to use, copy, modify and distribute this software and its
00034 documentation is hereby granted, provided that both the copyright
00035 notice and this permission notice appear in all copies of the
00036 software, derivative works or modified versions, and any portions
00037 thereof, and that both notices appear in supporting documentation.
00038 
00039 THE AUTHORS AND THE COMPUTER SCIENCES DEPARTMENT OF THE UNIVERSITY
00040 OF WISCONSIN - MADISON ALLOW FREE USE OF THIS SOFTWARE IN ITS
00041 "AS IS" CONDITION, AND THEY DISCLAIM ANY LIABILITY OF ANY KIND
00042 FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
00043 
00044 This software was developed with support by the Advanced Research
00045 Project Agency, ARPA order number 018 (formerly 8230), monitored by
00046 the U.S. Army Research Laboratory under contract DAAB07-91-C-Q518.
00047 Further funding for this work was provided by DARPA through
00048 Rome Research Laboratory Contract No. F30602-97-2-0247.
00049 
00050 */
00051 
00052 #ifndef __MEM_BLOCK_H
00053 #define __MEM_BLOCK_H
00054 
00055 /**\cond skip */
00056 #include <stddef.h>
00057 #include <cassert>
00058 #include "w_base.h"
00059 
00060 #define NORET
00061 
00062 /* NOTE: The classes defined here are non-template helpers for the
00063    template-based block_pool class. Because they are only intended to
00064    be used from template code, they accept "template" parameters with
00065    every call instead of storing them internally: register-passing
00066    from the template code is less expensive than loading the value
00067    from memory, and also minimizes per-block space overhead.
00068  */
00069 
00070 namespace memory_block {
00071 #if 0
00072 } // keep emacs happy...
00073 #endif
00074 
00075 /* GCC can't handle zero length arrays, but has an extension to handle
00076    unsized ones at the end of a struct. Meanwhile, SunStudio can't
00077    handle unsized arrays, but has an extension to allow zero-length
00078    arrays.
00079  */
00080 #ifdef __GNUC__
00081 #define EMPTY_ARRAY_DIM
00082 #else
00083 #define EMPTY_ARRAY_DIM 0
00084 #endif
00085 
00086 // forward decl...
00087 struct block_list;
00088 
00089 /* A bitmap allocator.
00090 
00091    This class tracks the bookkeeping of chip allocation while leaving
00092    the corresponding memory management to someone else. The
00093    implementation requires that chip allocation be single-threaded
00094    (presumably by some owner thread), but is thread-safe with respect
00095    to deallocation.
00096 
00097    A given "chip" may be in one of three states:
00098    
00099        USABLE: available to be allocated
00100     
00101     IN-USE: allocated but not yet freed
00102     
00103     ZOMBIE: freed more recently than the last recycling pass
00104 
00105    Allocation is double-buffered in a sense: at the beginning of each
00106    allocation round, the owning thread unions the current set of
00107    zombie chips into the usable set; in-use chips are ignored.
00108 
00109    The class has two members to support higher-level functionality:
00110    
00111        _owner: an opaque pointer which is used to verify that blocks
00112            are being released properly
00113         
00114     _next: an embedded linked list node for use by the owner and
00115         otherwise ignored by the implementation
00116  */
00117 struct block_bits {
00118     
00119     typedef w_base_t::uint8_t     bitmap; 
00120 
00121     enum         { MAX_CHIPS=8*sizeof(bitmap) };
00122     
00123     NORET        block_bits(size_t chip_count);
00124     
00125     size_t         acquire(size_t chip_count);
00126 
00127     void        release(size_t idx, size_t chip_count);
00128     
00129     static
00130     bitmap        create_mask(size_t bits_set);
00131 
00132     bitmap volatile*     usable_chips() { return &_usable_chips; }
00133     
00134     size_t        usable_count() const { return _popc(_usable_chips); }
00135     size_t        zombie_count() const { return _popc(_zombie_chips); }
00136 
00137     void        recycle();
00138     
00139     static
00140     size_t        _popc(bitmap bm);
00141 
00142     bitmap        _usable_chips; // available as of last recycling (1thr)
00143     bitmap volatile    _zombie_chips; // deallocations since last recycling (racy)
00144 };
00145 
00146 /* Control structure for one block of allocatable memory.
00147 
00148    The memory is assumed to be /block_size/ bytes long and must be
00149    aligned on a /block_size/-sized boundary. The implementation
00150    exploits the block's alignment to compute the block control for
00151    pointers being released, meaning there is no per-chip space
00152    overhead.
00153 
00154    One caveat of the implicit control block pointer approach is that
00155    the caller is responsible to ensure that any chip passed to
00156    /release()/ is actually part of a block (presumably by verifying
00157    that the address range is inside an appropriate memory pool).
00158 
00159    This class manages memory only very loosely: distributing and
00160    accepting the return of /chip_size/-sized chips. At no time does it
00161    access any of the memory it manages.
00162  */
00163 struct block {
00164     
00165     NORET        block(size_t chip_size, size_t chip_count, size_t block_size);
00166     
00167     void*         acquire(size_t chip_size, size_t chip_count, size_t block_size);
00168 
00169     // WARNING: the caller must ensure that ptr is in a valid memory range
00170     static
00171     void        release(void* ptr, size_t chip_size, size_t chip_count, size_t block_size);
00172 
00173     void        recycle() { _bits.recycle(); }
00174 
00175     char*        _get(size_t idx, size_t chip_size);
00176     
00177     block_bits        _bits;
00178     block_list*     _owner;
00179     block*         _next;
00180     char        _data[EMPTY_ARRAY_DIM];
00181 };
00182 
00183 
00184 struct block_pool {
00185     block*        acquire_block(block_list* owner);
00186     block*        release_block(block* b);
00187 
00188     // true if /ptr/ points to data inside some block managed by this pool
00189     virtual bool    validate_pointer(void* ptr)=0;
00190     
00191     virtual NORET    ~block_pool() { }
00192     
00193 protected:
00194     
00195     virtual block*     _acquire_block()=0;
00196     // takes back b, then returns b->_next
00197     virtual void     _release_block(block* b)=0;
00198 };
00199 
00200 struct block_list {
00201     NORET        block_list(block_pool* pool, size_t chip_size,
00202                    size_t chip_count, size_t block_size);
00203     
00204     NORET        ~block_list();
00205     
00206     void*         acquire(size_t chip_size, size_t chip_count, size_t block_size);
00207     block*        acquire_block(size_t block_size);
00208 
00209     void*        _slow_acquire(size_t chip_size, size_t chip_count, size_t block_size);
00210     void        _change_blocks(size_t chip_size, size_t chip_count, size_t block_size);
00211 
00212     block        _fake_block;
00213     block*        _tail;
00214     block_pool*        _pool;
00215     size_t        _hit_count;
00216     double        _avg_hit_rate;
00217     
00218 };
00219 
00220 
00221 inline
00222 block* block_pool::acquire_block(block_list* owner) {
00223     block* b = _acquire_block();
00224     b->_owner = owner;
00225     b->_next = 0;
00226     b->_bits.recycle();
00227     return b;
00228 }
00229 
00230 inline
00231 block* block_pool::release_block(block* b) {
00232     assert(validate_pointer(b));
00233     block* next = b->_next;
00234     b->_next = 0;
00235     b->_owner = 0;
00236     _release_block(b);
00237     return next;
00238 }
00239 
00240 
00241 /* A compile-time predicate.
00242 
00243    Compilation will fail for B=false because only B=true has a definition
00244 */
00245 template<bool B>
00246 struct fail_unless;
00247 
00248 template<>
00249 struct fail_unless<true> {
00250     static bool valid() { return true; }
00251 };
00252 
00253 
00254 
00255 /* A compile-time bounds checker.
00256 
00257    Fails to compile unless /L <= N <= U/
00258  */
00259 template<int N, int L, int U>
00260 struct bounds_check : fail_unless<(L <= N) && (N <= U)> {
00261     static bool valid() { return true; }
00262 };
00263 
00264 
00265 
00266 /* A template class which, given some positive compile-time constant
00267    integer /x/, computes the compile-time constant value of
00268    /floor(log2(x))/
00269  */
00270 
00271 template <int N>
00272 struct meta_log2 : fail_unless<(N > 2)> {
00273     enum { VALUE=1+meta_log2<N/2>::VALUE };
00274 };
00275 
00276 template<>
00277 struct meta_log2<2> {
00278     enum { VALUE=1 };
00279 };
00280 
00281 template<>
00282 struct meta_log2<1> {
00283     enum { VALUE=0 };
00284 };
00285 
00286 template<int A, int B>
00287 struct meta_min {
00288     enum { VALUE = (A < B)? A : B };
00289 };
00290 
00291 /* A template class which computes the optimial block size. Too large
00292    a block and the bitmap can't reach all the objects that fit,
00293    wasting space. Too small and the bitmap is mostly empty, leading to
00294    high overheads (in both space and time).
00295 
00296    For any given parameters there exists only one value of /BYTES/
00297    which is a power of two and utilizes 50% < util <= 100% of a
00298    block_bit's chips. 
00299  */
00300 template<int ChipSize, int OverheadBytes=sizeof(memory_block::block), int MaxChips=block_bits::MAX_CHIPS>
00301 struct meta_block_size : fail_unless<(ChipSize > 0 && OverheadBytes >= 0)> {
00302     enum { CHIP_SIZE    = ChipSize };
00303     enum { OVERHEAD     = OverheadBytes };
00304     enum { MAX_CHIPS     = MaxChips };
00305     enum { MIN_CHIPS     = MAX_CHIPS/2 + 1 };
00306     enum { BYTES_NEEDED = MIN_CHIPS*ChipSize+OverheadBytes };
00307     enum { LOG2     = meta_log2<2*BYTES_NEEDED-1>::VALUE };
00308     
00309     enum { BYTES     = 1 << LOG2 };
00310     fail_unless<((BYTES &- BYTES) == BYTES)>     power_of_two;
00311     
00312     /* ugly corner case...
00313 
00314        if chips are small compared to overhead then we can end up with
00315        space for more than MAX_CHIPS. However, cutting the block size
00316        in half wouldn't leave enough chips behind so we're stuck.
00317 
00318        Note that this wasted space would be small compared with the
00319        enormous overhead that causes the situation in the first place.
00320      */    
00321     enum { REAL_COUNT     = (BYTES-OverheadBytes)/ChipSize };
00322     fail_unless<((OVERHEAD + MIN_CHIPS*ChipSize) > (int)BYTES/2)> sane_chip_count;
00323     
00324     enum { COUNT     = meta_min<MAX_CHIPS, REAL_COUNT>::VALUE };
00325     bounds_check<COUNT, MIN_CHIPS, MAX_CHIPS>     bounds;
00326 
00327 };
00328 
00329 } // namespace memory_block
00330 
00331 /**\endcond skip */
00332 
00333 #endif

Generated on Wed Jul 7 17:22:32 2010 for Shore Storage Manager by  doxygen 1.4.7