Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

more/gen/recycler.h

Go to the documentation of this file.
00001 //  Copyright (C) 2001--2002  Petter Urkedal
00002 //
00003 //  This file is free software; you can redistribute it and/or modify
00004 //  it under the terms of the GNU General Public License as published by
00005 //  the Free Software Foundation; either version 2 of the License, or
00006 //  (at your option) any later version.
00007 //
00008 //  This file is distributed in the hope that it will be useful,
00009 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 //  GNU General Public License for more details.
00012 //
00013 //  You should have received a copy of the GNU General Public License
00014 //  along with this program; if not, write to the Free Software
00015 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00016 //
00017 //  As a special exception, you may use this file as part of a free
00018 //  software library without restriction.  Specifically, if other files
00019 //  instantiate templates or use macros or inline functions from this
00020 //  file, or you compile this file and link it with other files to
00021 //  produce an executable, this file does not by itself cause the
00022 //  resulting executable to be covered by the GNU General Public
00023 //  License.  This exception does not however invalidate any other
00024 //  reasons why the executable file might be covered by the GNU General
00025 //  Public License.
00026 //
00027 //  $Id: recycler.h,v 1.2 2002/08/24 13:56:07 petter_urkedal Exp $
00028 
00029 
00030 #ifndef MORE_GEN_RECYCLER_H
00031 #define MORE_GEN_RECYCLER_H
00032 
00033 #include <more/gen/hook.h>
00034 #include <more/gen/ring.h>
00035 #include <more/gen/functional.h>
00036 #include <list>
00037 #include <stdexcept>
00038 #include <string>
00039 #ifdef MORE_ENABLE_LOGGING
00040 #  include <iostream>
00041 #endif
00042 
00043 
00044 namespace more {
00045 namespace gen {
00046 
00047   /** To enable some logging, set \c opt_recycler_enable_log to true.
00048    *  \c MORE_ENABLE_LOGGING must be defined for this to take full effect.
00049    */
00050   extern bool opt_recycler_do_log;
00051 
00052   class recycler_base;
00053 
00054   /** \class recycler_group recycler.h more/recycler.h
00055    *  Shared data for a group of recyclers whose objects reference each other.
00056    */
00057   struct recycler_group
00058   {
00059       recycler_group() {}
00060       explicit recycler_group(void (*mark_all)())
00061       {
00062           mark_hook.insert(mark_all);
00063       }
00064       ~recycler_group();
00065 
00066       /** Register \a mark_all to be called in the mark phase of the
00067        *  GC.  The set of markers registered are assumed to mark all
00068        *  objects which have been allocated by recyclers of this group
00069        *  and which are still in use.
00070        */
00071       void register_marker(void (*mark_all)()) { mark_hook.insert(mark_all); }
00072 
00073       /** Force a garbage collection for this group. */
00074       void collect();
00075 
00076     private:
00077       hook<> mark_hook;
00078       std::list<recycler_base*> recyclers;
00079 
00080       friend class recycler_base;
00081   };
00082 
00083   /** \class recycler_base recycler.h more/recycler.h
00084    *  Base class for \c recycler.
00085    *
00086    *  This is used to communicate between \c recycler template
00087    *  instantiations.
00088    */
00089   struct recycler_base
00090   {
00091       typedef unsigned long size_type;
00092 
00093       /** Construct a recycler which is tied to the other recyclers of
00094        *  \a rg. */
00095       explicit recycler_base(recycler_group& rg)
00096           : group(&rg)
00097       {
00098           group->recyclers.push_back(this);
00099       }
00100 
00101       /** Destruct the recycler, and remove it from the group. */
00102       virtual ~recycler_base();
00103 
00104     protected:
00105       virtual void clear_marks() = 0;
00106 
00107       void collect() { group->collect(); }
00108 
00109     private:
00110       recycler_group* group;
00111 
00112       friend class recycler_group;
00113   };
00114 
00115   /** \class recycler recycler.h more/recycler.h
00116    *  A type for recycling garbage collected objects.
00117    *
00118    *  \pre For any object \c x of type \c T, \c x.unset_mark() must be
00119    *  valid, and \c x.is_marked() must be valid and return bool.  \c
00120    *  x.is_marked() shall return \c false after construction of \c x
00121    *  and after \c x.unset_mark() is called.  After the container has
00122    *  requested the client to mark all used objects, \c x.is_marked()
00123    *  shall return true iff the object is referred to by the client.
00124    *  Note that also the copy assignment of T shall preserve the mark
00125    *  bit, i.e. it shall \e not copy it.
00126    *
00127    *  Presently the recycler never frees chunks of memory, so its
00128    *  memory usage will stay at its peek.  One method to free chunks,
00129    *  is to copy all objects unsing a new recycler, and destruct the
00130    *  old recycler.
00131    *
00132    *  \warning The interface of this type may change.  It is intended
00133    *  for internal use, at least for the moment.
00134    *
00135    *  \todo Need a mechanism to merge chunks after a peek memory
00136    *  consumption.  This will involve copying object, thus T must be
00137    *  copyable, and renaming pointers, which requires a function
00138    *  similar to the \c mark_all argument to the ctor.  Also, see if
00139    *  this class can be isolated from this file, for more general
00140    *  application.
00141    */
00142   template<typename T>
00143     struct recycler : recycler_base
00144     {
00145         typedef std::size_t size_type;
00146         typedef std::ptrdiff_t difference_type;
00147         typedef T* pointer;
00148         typedef T const* const_pointer;
00149         typedef T& reference;
00150         typedef T const& const_reference;
00151         typedef T value_type;
00152         template<typename U> struct rebind { typedef recycler<U> other; };
00153 
00154         // Since we use a different recycler for each type, keep the
00155         // value_count low, so that we do not waist too much memory,
00156         // but still high enough that the per-chunk overhead is
00157         // negligible.
00158         static const size_type value_count = 16;
00159 
00160         //@{
00161         /** The filling of chunks before GC is about \c
00162          *  filling_num/filling_denom.  This is a compile-time option
00163          *  which must be set in the source code.
00164          */
00165         static const int filling_num = 2;
00166         static const int filling_denom = 3;
00167         //@}
00168 
00169       private:
00170         struct chunk {
00171             chunk() {}
00172           private:
00173             chunk(chunk const&) {}
00174           public:
00175             value_type array[value_count];
00176         };
00177 
00178       public:
00179         /** Construct a new recycler.
00180          *
00181          *  \param mark_all is called during a GC to mark the objects,
00182          *  and it is assumed that it marks all used objects in this
00183          *  recycler and in all recyclers tied to it.
00184          *
00185          *  \param tied if supplied means that there are references
00186          *  between this recycler and tied, so that that GC must be
00187          *  synchronized.
00188          */
00189         explicit recycler(recycler_group& x)
00190             : recycler_base(x),
00191               n_allocs_since_gc(0),
00192               n_chunks_since_gc(0) {
00193             r_chunk.push_front();
00194             it_chunk = r_chunk.principal();
00195             v_cur = it_chunk->array;
00196             v_end = v_cur + value_count;
00197             v_gc = v_cur;
00198         }
00199       private: // XXX these must be implemented
00200         recycler(recycler const& rcyc) {}
00201         template<typename U> recycler(recycler<U> const&) {}
00202       public:
00203 
00204         /** Returns \c &r. */
00205         pointer address(reference r) const { return &r; }
00206         /** Returns \c &r. */
00207         const_pointer address(const_reference r) const { return &r; }
00208 
00209         /** Return an object which is no longer in use.  Note that it
00210          *  may contain and old value, so is may be necessary to clear
00211          *  it.  This can be done by assigning a new value using
00212          *  \c construct. */
00213         T* allocate(size_type n, const_pointer p = 0) {
00214 #ifndef MORE_NDEBUG
00215             if (n != 1)
00216                 throw std::logic_error("more::recycler: This allocator can "
00217                                        "not handle arrays.");
00218 #endif
00219             while (v_cur != v_end) {
00220                 if (!v_cur->is_marked()) {
00221                     ++n_allocs_since_gc;
00222                     return v_cur++;
00223                 }
00224                 ++v_cur;
00225             }
00226             return expand_and_allocate();
00227         }
00228 
00229         /** Does nothing. */
00230         void deallocate(pointer p, size_type n) {}
00231 
00232         /** Does <tt>p->~T(); new((void*)p) T;</tt> . */
00233         void construct(T* p) { p->~T(); new((void*)p) T; }
00234 
00235         /** Does <tt>p->~T(); new((void*)p) T(x);</tt> . */
00236         void construct(T* p, T const& x) { p->~T(); new((void*)p) T(x); }
00237 
00238         /** Does nothing. */
00239         void destroy(T*) {}
00240 
00241         /** Returns 1. */
00242         size_type max_size() { return 1; }
00243 
00244         template<typename Function>
00245           void for_each(Function f) {
00246               chunk_iterator it = it_chunk;
00247               do {
00248                   for (value_type* v = it->array;
00249                        v != it->array + value_count; ++v)
00250                       f(v);
00251               } while (++it != it_chunk);
00252           }
00253 
00254       private:
00255         typedef ring<chunk> chunk_container;
00256         typedef typename chunk_container::iterator chunk_iterator;
00257 
00258         T* expand_and_allocate() {
00259             while (v_cur != v_gc) {
00260                 ++it_chunk;
00261                 ++n_chunks_since_gc;
00262                 v_cur = it_chunk->array;
00263                 v_end = v_cur + value_count;
00264                 if (v_cur <= v_gc && v_gc < v_end)
00265                     v_end = v_gc;
00266 
00267               redo:
00268                 while (v_cur != v_end) {
00269                     if (!v_cur->is_marked()) {
00270                         ++n_allocs_since_gc;
00271                         return v_cur++;
00272                     }
00273                     ++v_cur;
00274                 }
00275             }
00276             // n_skips = n_allocs + n_misses
00277             size_type n_skips
00278                 = n_chunks_since_gc*value_count
00279                 + (v_cur - it_chunk->array);
00280 #ifdef MORE_ENABLE_LOGGING
00281             if (opt_recycler_do_log)
00282                 std::clog << "more::recycler: Availability ratio = "
00283                           << n_allocs_since_gc << '/' << n_skips
00284                           << ". ";
00285 #endif
00286             if (n_allocs_since_gc*filling_denom
00287                 >= n_skips*(filling_denom - filling_num)) {
00288 #ifdef MORE_ENABLE_LOGGING
00289                 std::clog << "Collecting.\n";
00290                 std::clog.flush();
00291 #endif
00292                 collect();
00293                 goto redo;
00294             }
00295             else {
00296 #ifdef MORE_ENABLE_LOGGING
00297                 std::clog << "Expanding.\n";
00298                 std::clog.flush();
00299 #endif
00300                 it_chunk = r_chunk.insert(it_chunk);
00301                 v_cur = it_chunk->array;
00302                 v_end = v_cur + value_count;
00303                 ++n_allocs_since_gc;
00304                 return v_cur++;
00305             }
00306         }
00307 
00308         virtual void clear_marks() {
00309             chunk_iterator it = it_chunk;
00310             do {
00311                 for (value_type* v = it->array;
00312                      v != it->array + value_count; ++v)
00313                     v->unset_mark();
00314             } while (++it != it_chunk);
00315             n_allocs_since_gc = 0;
00316             n_chunks_since_gc = 0;
00317             v_gc = v_cur;
00318             v_end = it_chunk->array + value_count;
00319         }
00320 
00321         static void clear_marks_helper(chunk* ch) {
00322             while (ch) {
00323                 for (value_type* v = ch->array;
00324                      v != ch->array + value_count; ++v)
00325                     v->unset_mark();
00326                 ch = ch->prev;
00327             }
00328         }
00329 
00330         chunk_container r_chunk;
00331         chunk_iterator  it_chunk;
00332 
00333         value_type* v_cur;
00334         value_type* v_end;
00335         value_type* v_gc;
00336 
00337         size_type n_allocs_since_gc;
00338         size_type n_chunks_since_gc;
00339     };
00340 }} // more::gen
00341 
00342 #endif

Generated on Sat Sep 7 19:11:14 2002 for more with Doxygen 1.2.13.1. Doxygen 1.2.13.1 is written and copyright 1997-2002 by Dimitri van Heesch.