atomic_class_pool.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 
00024 #ifndef __ATOMIC_TRASH_STACK
00025 #define __ATOMIC_TRASH_STACK
00026 
00027 #include <atomic_templates.h>
00028 
00029 // for placement new support, which some users need
00030 #include <new>
00031 #include <cassert>
00032 #include <stdlib.h>
00033 #include "atomic_container.h"
00034 
00035 
00036 /** \brief A thread-safe memory pool based on the atomic container, used by
00037  * atomic_class_pool.
00038  *
00039  * \note this code is \e not used by 
00040  * the storage manager (or maintained) anymore. The
00041  * code is still distributed with releases only because it is used by an 
00042  * important client, the "kits" distributed by DIAS.
00043  *
00044  * Creates a new atomic_container with \e seed pre-allocated
00045  * untyped items of size \e nbytes each.
00046  * It is important to have a non-zero seed value so that 
00047  * atomic_container versioning works correctly.
00048  *
00049  * Maintains a global freelist of fixed-sized memory chunks to recycle
00050  * and provides a drop-in replacement for malloc() and free()
00051  *
00052  */
00053 struct atomic_preallocated_pool : protected atomic_container 
00054 {
00055     atomic_preallocated_pool(uint nbytes, long seed=128)
00056         : atomic_container(-sizeof(ptr)), _nbytes(nbytes+sizeof(ptr))
00057     {
00058         // start with a non-empty pool so threads don't race at the beginning
00059         ptr* head = NULL;
00060         for(int i=0; i < seed; i++) {
00061             vpn u = {alloc()};
00062             u.p->next = head;
00063             head = u.p;
00064         }
00065         for(int i=0; i < seed; i++) {
00066             ptr* p = head;
00067             head = head->next;
00068             dealloc(p);
00069         }
00070     }
00071     void* alloc() {
00072         void* val = pop();
00073         if(val) return val;
00074         
00075         vpn u = { malloc(_nbytes) };
00076         if(!u.v) u.v = null();
00077         return prepare(u);
00078     }
00079     void dealloc(void* val) { push(val); }
00080     
00081     ~atomic_preallocated_pool() {
00082         vpn val;
00083         while( (val.v=pop()) ) {
00084             val.n += _offset; // back up to the real start of the pointer
00085             free(val.v);
00086         }
00087     }
00088     
00089     uint const _nbytes;
00090 };
00091 
00092 // forward decls...
00093 template<class T>
00094 struct atomic_class_pool;
00095 template<class T>
00096 void* operator new(size_t nbytes, atomic_class_pool<T>& pool);
00097 template<class T>
00098 inline void operator delete(void* ptr, atomic_class_pool<T>& pool);
00099 
00100 /** \brief A thread-safe memory pool for typed objects, based on atomic_preallocated_pool.
00101  *
00102  * \note this code is \e not used by 
00103  * the storage manager (or maintained) anymore. The
00104  * code is still distributed with releases only because it is used by an 
00105  * important client, the "kits" distributed by DIAS.
00106  *
00107  * Provides a replacement for new/delete on the specific class. Note
00108  * that there's actually no way to prevent the user from allocating
00109  * whatever they want, but they will be unable to destroy anything but
00110  * the specified class (and its subclasses).
00111  *
00112  * \code
00113  * Example:
00114  *
00115  *        class foo { };
00116  *        atomic_class_pool<foo> pool;
00117  *        foo* f = new(pool) foo;
00118  *        pool.destroy(f);
00119  * \endcode
00120  */
00121 template<class T>
00122 struct atomic_class_pool : protected atomic_preallocated_pool {
00123 
00124     /** \brief Create a pool for class T.
00125      *
00126      * By default the pool will hand out sizeof(T) bytes at a time; if
00127      * T is a base class and this pool is to be used with subclasses,
00128      * nbytes must be set at least as large as the largest
00129      * class. Oversized allocations will assert().
00130   * 
00131   * \note this code is \e not used by 
00132   * the storage manager (or maintained) anymore. The
00133   * code is still distributed with releases only because it is used by an 
00134   * important client, the "kits" distributed by DIAS.
00135      */
00136     atomic_class_pool(long nbytes=sizeof(T), long seed=128)
00137         : atomic_preallocated_pool(nbytes, seed)
00138     {
00139     }
00140 
00141     /** \brief Destroys an object (by calling its destructor) and returns its
00142      * memory to the pool.
00143      *
00144      * Undefined behavior results if the object did not come from this
00145      * pool.
00146   * 
00147   * \note this code is \e not used by 
00148   * the storage manager (or maintained) anymore. The
00149   * code is still distributed with releases only because it is used by an 
00150   * important client, the "kits" distributed by DIAS.
00151      */
00152     void destroy(T* tptr) {
00153         // avoid pointer aliasing problems with the optimizer
00154         union { T* t; void* v; } u = {tptr};
00155 
00156         // destruct the object and deallocate its memory
00157         u.t->~T();
00158         dealloc(u.v);
00159     }
00160 
00161     /** \brief Return the object size given to the constructor.
00162      */
00163     uint nbytes() { return _nbytes; }
00164     
00165     // these guys need to access the underlying preallocated stack
00166     friend void* operator new<>(size_t, atomic_class_pool<T> &);
00167     friend void operator delete<>(void*, atomic_class_pool<T> &);
00168 };
00169 
00170 /** \brief WARNING: When finished, call pool.destroy(t) instead of delete.
00171  * 
00172  * \note this code is \e not used by 
00173  * the storage manager (or maintained) anymore. The
00174  * code is still distributed with releases only because it is used by an 
00175  * important client, the "kits" distributed by DIAS.
00176  *
00177  * \note use placement-style new with the pool. 
00178  * \code 
00179  * usage: T* t = new(pool) T(...)
00180  * \endcode
00181  */
00182 
00183 template<class T>
00184 inline void* operator new(size_t nbytes, atomic_class_pool<T>& pool) {
00185     assert(pool.nbytes() >= nbytes);
00186     return pool.alloc();
00187 }
00188 
00189 /**\brief Called automatically by the compiler if T's constructor throws
00190  * (otherwise memory would leak).
00191  *
00192  * \note this code is \e not used by 
00193  * the storage manager (or maintained) anymore. The
00194  * code is still distributed with releases only because it is used by an 
00195  * important client, the "kits" distributed by DIAS.
00196  *
00197  * Unfortunately, there is no "delete(pool)" syntax in C++ so the user
00198  * must still call pool.destroy()
00199  */
00200 template<class T>
00201 inline void operator delete(void* ptr, atomic_class_pool<T>& pool) {
00202     pool.dealloc(ptr);
00203 }
00204 
00205 #endif

Generated on Mon Jan 2 15:13:56 2012 for Shore Storage Manager by  doxygen 1.4.7