Actual source code: ALE_mem.hh
petsc-3.4.2 2013-07-02

      1: #ifndef included_ALE_mem_hh
      2: #define included_ALE_mem_hh
      3: // This should be included indirectly -- only by including ALE.hh

      5: #include <assert.h>
      6: #include <deque>
      7: #include <iostream>
      8: #include <map>
      9: #include <memory>
     10: #include <cstdlib>
     11: #include <typeinfo>
     12: #include <petscsys.h>
     13: #include <sieve/ALE_log.hh>

     15: #ifdef ALE_HAVE_CXX_ABI
     16: #include <cxxabi.h>
     17: #endif

     19: namespace ALE {
     20:   class MemoryLogger;
     21: }
     22: extern ALE::MemoryLogger Petsc_MemoryLogger;

     24: namespace ALE {
     25:   class MemoryLogger {
     26:   public:
     27:     struct Log {
     28:       long long num;
     29:       long long total;
     30:       std::map<std::string, long long> items;

     32:       Log(): num(0), total(0) {};
     33:     };
     34:     typedef std::map<std::string, std::pair<Log, Log> > stageLog;
     35:     typedef std::deque<std::string>                     names;
     36:   protected:
     37:     int      _debug;
     38:     MPI_Comm _comm;
     39:     int      rank;
     40:     names    stageNames;
     41:     stageLog stages;
     42:   public:
     43:     MemoryLogger(): _debug(0), _comm(MPI_COMM_NULL), rank(-1) {
     44:       stageNames.push_front("default");
     45:     };
     46:   public:
     47:     ~MemoryLogger() {};
     48:     static MemoryLogger& singleton() {
     49:       if (Petsc_MemoryLogger.comm() == MPI_COMM_NULL) {
     50:         Petsc_MemoryLogger.setComm(PETSC_COMM_WORLD);
     51:       }
     52:       return Petsc_MemoryLogger;
     53:     };
     54:     int  debug() {return _debug;};
     55:     void setDebug(int debug) {_debug = debug;};
     56:     MPI_Comm comm() {return _comm;};
     57:     void     setComm(MPI_Comm comm) {
     58:       _comm = comm;
     59:       MPI_Comm_rank(_comm, &rank);
     60:     };
     61:   public:
     62:     void stagePush(const std::string& name) {
     63:       for(names::const_iterator s_iter = stageNames.begin(); s_iter != stageNames.end(); ++s_iter) {
     64:         if (*s_iter == name) throw ALE::Exception(std::string("Cannot push duplicate stage name '")+name+std::string("'."));
     65:       }
     66:       stageNames.push_front(name);
     67:       if (_debug) {
     68:         std::cout << "["<<rank<<"]Pushing stage " << name << ":" << std::endl;
     69:         for(names::const_iterator s_iter = stageNames.begin(); s_iter != stageNames.end(); ++s_iter) {
     70:           std::cout << "["<<rank<<"]  " << *s_iter << ": " << stages[*s_iter].first.num  << " acalls  " << stages[*s_iter].first.total  << " bytes" << std::endl;
     71:           std::cout << "["<<rank<<"]  " << *s_iter << ": " << stages[*s_iter].second.num << " dcalls  " << stages[*s_iter].second.total << " bytes" << std::endl;
     72:         }
     73:       }
     74:     };
     75:     void stagePop() {
     76:       if (_debug) {
     77:         std::cout << "["<<rank<<"]Popping stage " << stageNames.front() << ":" << std::endl;
     78:         for(names::const_iterator s_iter = stageNames.begin(); s_iter != stageNames.end(); ++s_iter) {
     79:           std::cout << "["<<rank<<"]  " << *s_iter << ": " << stages[*s_iter].first.num  << " acalls  " << stages[*s_iter].first.total  << " bytes" << std::endl;
     80:           std::cout << "["<<rank<<"]  " << *s_iter << ": " << stages[*s_iter].second.num << " dcalls  " << stages[*s_iter].second.total << " bytes" << std::endl;
     81:         }
     82:       }
     83:       stageNames.pop_front();
     84:     };
     85:     void logAllocation(const std::string& className, int bytes) {
     86:       for(names::const_iterator s_iter = stageNames.begin(); s_iter != stageNames.end(); ++s_iter) {
     87:         logAllocation(*s_iter, className, bytes);
     88:       }
     89:     };
     90:     void logAllocation(const std::string& stage, const std::string& className, int bytes) {
     91:       if (_debug > 1) {std::cout << "["<<rank<<"]Allocating " << bytes << " bytes for class " << className << std::endl;}
     92:       stages[stage].first.num++;
     93:       stages[stage].first.total += bytes;
     94:       stages[stage].first.items[className] += bytes;
     95:     };
     96:     void logDeallocation(const std::string& className, int bytes) {
     97:       for(names::const_iterator s_iter = stageNames.begin(); s_iter != stageNames.end(); ++s_iter) {
     98:         logDeallocation(*s_iter, className, bytes);
     99:       }
    100:     };
    101:     void logDeallocation(const std::string& stage, const std::string& className, int bytes) {
    102:       if (_debug > 1) {std::cout << "["<<rank<<"]Deallocating " << bytes << " bytes for class " << className << std::endl;}
    103:       stages[stage].second.num++;
    104:       stages[stage].second.total += bytes;
    105:       stages[stage].second.items[className] += bytes;
    106:     };
    107:   public:
    108:     long long getNumAllocations() {return getNumAllocations(stageNames.front());};
    109:     long long getNumAllocations(const std::string& stage) {return stages[stage].first.num;};
    110:     long long getNumDeallocations() {return getNumDeallocations(stageNames.front());};
    111:     long long getNumDeallocations(const std::string& stage) {return stages[stage].second.num;};
    112:     long long getAllocationTotal() {return getAllocationTotal(stageNames.front());};
    113:     long long getAllocationTotal(const std::string& stage) {return stages[stage].first.total;};
    114:     long long getDeallocationTotal() {return getDeallocationTotal(stageNames.front());};
    115:     long long getDeallocationTotal(const std::string& stage) {return stages[stage].second.total;};
    116:   public:
    117:     void show() {
    118:       std::cout << "["<<rank<<"]Memory Stages:" << std::endl;
    119:       for(stageLog::const_iterator s_iter = stages.begin(); s_iter != stages.end(); ++s_iter) {
    120:         std::cout << "["<<rank<<"]  " << s_iter->first << ": " << s_iter->second.first.num  << " acalls  " << s_iter->second.first.total  << " bytes" << std::endl;
    121:         for(std::map<std::string, long long>::const_iterator i_iter = s_iter->second.first.items.begin(); i_iter != s_iter->second.first.items.end(); ++i_iter) {
    122:           std::cout << "["<<rank<<"]    " << i_iter->first << ": " << i_iter->second << " bytes" << std::endl;
    123:         }
    124:         std::cout << "["<<rank<<"]  " << s_iter->first << ": " << s_iter->second.second.num << " dcalls  " << s_iter->second.second.total << " bytes" << std::endl;
    125:         for(std::map<std::string, long long>::const_iterator i_iter = s_iter->second.second.items.begin(); i_iter != s_iter->second.second.items.end(); ++i_iter) {
    126:           std::cout << "["<<rank<<"]    " << i_iter->first << ": " << i_iter->second << " bytes" << std::endl;
    127:         }
    128:       }
    129:     };
    130:   public:
    131:     template<typename T>
    132:     static const char *getClassName() {
    133:       const std::type_info& id      = typeid(T);
    134:       char                 *id_name = const_cast<char *>(id.name());

    136: #ifdef ALE_HAVE_CXX_ABI
    137:       // If the C++ ABI API is available, we can use it to demangle the class name provided by type_info.
    138:       // Here we assume the industry standard C++ ABI as described in http://www.codesourcery.com/cxx-abi/abi.html.
    139:       int   status;
    140:       char *id_name_demangled = abi::__cxa_demangle(id.name(), NULL, NULL, &status);

    142:       if (!status) {
    143:         id_name = id_name_demangled;
    144:       }
    145: #endif
    146:       return id_name;
    147:     }
    148:     static void restoreClassName(const char * /* className */) {};
    149:   };

    151:   template<class T>
    152:   class malloc_allocator
    153:   {
    154:   public:
    155:     typedef T                 value_type;
    156:     typedef value_type*       pointer;
    157:     typedef const value_type* const_pointer;
    158:     typedef value_type&       reference;
    159:     typedef const value_type& const_reference;
    160:     typedef std::size_t       size_type;
    161:     typedef std::ptrdiff_t    difference_type;
    162:   public:
    163:     template <class U>
    164:     struct rebind {typedef malloc_allocator<U> other;};
    165:   protected:
    166:     int         numAllocs;
    167:     const char *className;
    168:   public:
    169:     int sz;
    170:   public:
    171: #ifdef ALE_MEM_LOGGING
    172:     malloc_allocator() : numAllocs(0) {className = ALE::MemoryLogger::getClassName<T>();sz = sizeof(value_type);}
    173:     malloc_allocator(const malloc_allocator&) : numAllocs(0) {className = ALE::MemoryLogger::getClassName<T>();sz = sizeof(value_type);}
    174:     template <class U>
    175:     malloc_allocator(const malloc_allocator<U>&) : numAllocs(0) {className = ALE::MemoryLogger::getClassName<T>();sz = sizeof(value_type);}
    176:     ~malloc_allocator() {ALE::MemoryLogger::restoreClassName(className);}
    177: #else
    178:     malloc_allocator() : numAllocs(0) {sz = sizeof(value_type);}
    179:     malloc_allocator(const malloc_allocator&) : numAllocs(0) {sz = sizeof(value_type);}
    180:     template <class U>
    181:     malloc_allocator(const malloc_allocator<U>&) : numAllocs(0) {sz = sizeof(value_type);}
    182:     ~malloc_allocator() {}
    183: #endif
    184:   public:
    185:     pointer address(reference x) const {return &x;}
    186:     // For some reason the goddamn MS compiler does not like this function
    187:     //const_pointer address(const_reference x) const {return &x;}

    189:     pointer allocate(size_type n, const_pointer = 0) {
    190:       assert(n >= 0);
    191: #ifdef ALE_MEM_LOGGING
    192:       ALE::MemoryLogger::singleton().logAllocation(className, n * sizeof(T));
    193: #endif
    194:       numAllocs++;
    195:       void *p = std::malloc(n * sizeof(T));
    196:       if (!p) throw std::bad_alloc();
    197:       return static_cast<pointer>(p);
    198:     }

    200: #ifdef ALE_MEM_LOGGING
    201:     void deallocate(pointer p, size_type n) {
    202:       ALE::MemoryLogger::singleton().logDeallocation(className, n * sizeof(T));
    203:       std::free(p);
    204:     }
    205: #else
    206:     void deallocate(pointer p, size_type) {
    207:       std::free(p);
    208:     }
    209: #endif

    211:     size_type max_size() const {return static_cast<size_type>(-1) / sizeof(T);}

    213:     void construct(pointer p, const value_type& x) {new(p) value_type(x);}

    215:     void destroy(pointer p) {p->~value_type();}
    216:   public:
    217:     pointer create(const value_type& x = value_type()) {
    218:       pointer p = (pointer) allocate(1);
    219:       construct(p, x);
    220:       return p;
    221:     };

    223:     void del(pointer p) {
    224:       destroy(p);
    225:       deallocate(p, 1);
    226:     };

    228:     // This is just to be compatible with Dmitry's weird crap for now
    229:     void del(pointer p, size_type size) {
    230:       if (size != sizeof(value_type)) throw std::exception();
    231:       destroy(p);
    232:       deallocate(p, 1);
    233:     };
    234:   private:
    235:     void operator=(const malloc_allocator&);
    236:   };

    238:   template<> class malloc_allocator<void>
    239:   {
    240:     typedef void        value_type;
    241:     typedef void*       pointer;
    242:     typedef const void* const_pointer;

    244:     template <class U>
    245:     struct rebind {typedef malloc_allocator<U> other;};
    246:   };

    248:   template <class T>
    249:   inline bool operator==(const malloc_allocator<T>&, const malloc_allocator<T>&) {
    250:     return true;
    251:   };

    253:   template <class T>
    254:   inline bool operator!=(const malloc_allocator<T>&, const malloc_allocator<T>&) {
    255:     return false;
    256:   };

    258:   template <class T>
    259:   static const char *getClassName() {
    260:     const std::type_info& id = typeid(T);
    261:     const char *id_name;

    263: #ifdef ALE_HAVE_CXX_ABI
    264:       // If the C++ ABI API is available, we can use it to demangle the class name provided by type_info.
    265:       // Here we assume the industry standard C++ ABI as described in http://www.codesourcery.com/cxx-abi/abi.html.
    266:       int   status;
    267:       char *id_name_demangled = abi::__cxa_demangle(id.name(), NULL, NULL, &status);

    269:       if (status != 0) {
    270:         id_name = id.name();
    271:       } else {
    272:         id_name = id_name_demangled;
    273:       }
    274: #else
    275:       id_name = id.name();
    276: #endif
    277:       return id_name;
    278:   };
    279:   template <class T>
    280:   static const char *getClassName(const T * /* obj */) {
    281:     return getClassName<T>();
    282:   };
    283: #ifdef ALE_HAVE_CXX_ABI
    284:   template<class T>
    285:   static void restoreClassName(const char *id_name) {
    286:     // Free the name malloc'ed by __cxa_demangle
    287:     free((char *) id_name);
    288:   };
    289: #else
    290:   template<class T>
    291:   static void restoreClassName(const char *) {};
    292: #endif
    293:   template<class T>
    294:   static void restoreClassName(const T * /* obj */, const char *id_name) {restoreClassName<T>(id_name);};

    296:   // This UNIVERSAL allocator class is static and provides allocation/deallocation services to all allocators defined below.
    297:   class universal_allocator {
    298:   public:
    299:     typedef std::size_t size_type;
    300:     static char*     allocate(const size_type& sz);
    301:     static void      deallocate(char *p, const size_type& sz);
    302:     static size_type max_size();
    303:   };

    305:   // This allocator implements create and del methods, that act roughly as new and delete in that they invoke a constructor/destructor
    306:   // in addition to memory allocation/deallocation.
    307:   // An additional (and potentially dangerous) feature allows an object of any type to be deleted so long as its size has been provided.
    308:   template <class T>
    309:   class polymorphic_allocator {
    310:   public:
    311:     typedef typename std::allocator<T> Alloc;
    312:     // A specific allocator -- alloc -- of type Alloc is used to define the correct types and implement methods
    313:     // that do not allocate/deallocate memory themselves -- the universal _alloc is used for that (and only that).
    314:     // The relative size sz is used to calculate the amount of memory to request from _alloc to satisfy a request to alloc.
    315:     typedef typename Alloc::size_type       size_type;
    316:     typedef typename Alloc::difference_type difference_type;
    317:     typedef typename Alloc::pointer         pointer;
    318:     typedef typename Alloc::const_pointer   const_pointer;
    319:     typedef typename Alloc::reference       reference;
    320:     typedef typename Alloc::const_reference const_reference;
    321:     typedef typename Alloc::value_type      value_type;

    323:     static Alloc alloc;                            // The underlying specific allocator
    324:     static typename Alloc::size_type sz;           // The size of T universal units of char

    326:     polymorphic_allocator()                                    {};
    327:     polymorphic_allocator(const polymorphic_allocator& a)      {};
    328:     template <class TT>
    329:     polymorphic_allocator(const polymorphic_allocator<TT>& aa){}
    330:     ~polymorphic_allocator() {};

    332:     // Reproducing the standard allocator interface
    333:     pointer       address(reference _x) const          { return alloc.address(_x);                                    };
    334:     const_pointer address(const_reference _x) const    { return alloc.address(_x);                                    };
    335:     T*            allocate(size_type _n)               { return (T*)universal_allocator::allocate(_n*sz);            };
    336:     void          deallocate(pointer _p, size_type _n) { universal_allocator::deallocate((char*)_p, _n*sz);           };
    337:     void          construct(pointer _p, const T& _val) { alloc.construct(_p, _val);                                   };
    338:     void          destroy(pointer _p)                  { alloc.destroy(_p);                                           };
    339:     size_type     max_size() const                     { return (size_type)floor(universal_allocator::max_size()/sz); };
    340:     // conversion typedef
    341:     template <class TT>
    342:     struct rebind { typedef polymorphic_allocator<TT> other;};

    344:     T*  create(const T& _val = T());
    345:     void del(T* _p);
    346:     template<class TT> void del(TT* _p, size_type _sz);
    347:   };

    349:   template <class T>
    350:   typename polymorphic_allocator<T>::Alloc polymorphic_allocator<T>::alloc;

    352:   //IMPORTANT: allocator 'sz' calculation takes place here
    353:   template <class T>
    354:   typename polymorphic_allocator<T>::size_type polymorphic_allocator<T>::sz =
    355:     (typename polymorphic_allocator<T>::size_type)(ceil(sizeof(T)/sizeof(char)));

    357:   template <class T>
    358:   T* polymorphic_allocator<T>::create(const T& _val) {
    359:     // First, allocate space for a single object
    360:     T* _p = (T*)universal_allocator::allocate(sz);
    361:     // Construct an object in the provided space using the provided initial value
    362:     this->alloc.construct(_p,  _val);
    363:     return _p;
    364:   }

    366:   template <class T>
    367:   void polymorphic_allocator<T>::del(T* _p) {
    368:     _p->~T();
    369:     universal_allocator::deallocate((char*)_p, polymorphic_allocator<T>::sz);
    370:   }

    372:   template <class T> template <class TT>
    373:   void polymorphic_allocator<T>::del(TT* _p, size_type _sz) {
    374:     _p->~TT();
    375:     universal_allocator::deallocate((char*)_p, _sz);
    376:   }

    379:   // An allocator all of whose events (allocation, deallocation, new, delete) are logged using ALE_log facilities.
    380:   // O is true if this is an Obj allocator (that's the intended use, anyhow).
    381:   template <class T, bool O = false>
    382:   class logged_allocator : public polymorphic_allocator<T> {
    383:   private:
    384:     static bool        _log_initialized;
    385:     static LogCookie   _cookie;
    386:     static int         _allocate_event;
    387:     static int         _deallocate_event;
    388:     static int         _construct_event;
    389:     static int         _destroy_event;
    390:     static int         _create_event;
    391:     static int         _del_event;
    392:     //
    393:     static void     __log_initialize();
    394:     static LogEvent __log_event_register(const char *class_name, const char *event_name);
    395: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    396:     // FIX: should PETSc memory logging machinery be wrapped by ALE_log like the rest of the logging stuff?
    397:     PetscObject _petscObj; // this object is used to log memory in PETSc
    398: #endif
    399:     void __alloc_initialize();
    400:     void __alloc_finalize();
    401:   public:
    402:     // Present the correct allocator interface
    403:     typedef typename polymorphic_allocator<T>::size_type       size_type;
    404:     typedef typename polymorphic_allocator<T>::difference_type difference_type;
    405:     typedef typename polymorphic_allocator<T>::pointer         pointer;
    406:     typedef typename polymorphic_allocator<T>::const_pointer   const_pointer;
    407:     typedef typename polymorphic_allocator<T>::reference       reference;
    408:     typedef typename polymorphic_allocator<T>::const_reference const_reference;
    409:     typedef typename polymorphic_allocator<T>::value_type      value_type;
    410:     //
    411:     logged_allocator()                                   : polymorphic_allocator<T>()  {__log_initialize(); __alloc_initialize();};
    412:     logged_allocator(const logged_allocator& a)          : polymorphic_allocator<T>(a) {__log_initialize(); __alloc_initialize();};
    413:     template <class TT>
    414:     logged_allocator(const logged_allocator<TT>& aa)    : polymorphic_allocator<T>(aa){__log_initialize(); __alloc_initialize();}
    415:     ~logged_allocator() {__alloc_finalize();};
    416:     // conversion typedef
    417:     template <class TT>
    418:     struct rebind { typedef logged_allocator<TT> other;};

    420:     T*   allocate(size_type _n);
    421:     void deallocate(T*  _p, size_type _n);
    422:     void construct(T* _p, const T& _val);
    423:     void destroy(T* _p);

    425:     T*  create(const T& _val = T());
    426:     void del(T*  _p);
    427:     template <class TT> void del(TT* _p, size_type _sz);
    428:   };

    430:   template <class T, bool O>
    431:   bool logged_allocator<T, O>::_log_initialized(false);
    432:   template <class T, bool O>
    433:   LogCookie logged_allocator<T,O>::_cookie(0);
    434:   template <class T, bool O>
    435:   int logged_allocator<T, O>::_allocate_event(0);
    436:   template <class T, bool O>
    437:   int logged_allocator<T, O>::_deallocate_event(0);
    438:   template <class T, bool O>
    439:   int logged_allocator<T, O>::_construct_event(0);
    440:   template <class T, bool O>
    441:   int logged_allocator<T, O>::_destroy_event(0);
    442:   template <class T, bool O>
    443:   int logged_allocator<T, O>::_create_event(0);
    444:   template <class T, bool O>
    445:   int logged_allocator<T, O>::_del_event(0);

    447:   template <class T, bool O>
    448:   void logged_allocator<T, O>::__log_initialize() {
    449:     if(!logged_allocator::_log_initialized) {
    450:       // First of all we make sure PETSc is initialized
    451:       PetscBool      flag;
    452:       PetscErrorCode PetscInitialized(&flag);CHKERROR(ierr, "Error in PetscInitialized");
    453:       if(!flag) {
    454:         // I guess it would be nice to initialize PETSc here, but we'd need argv/argc here
    455:         throw ALE::Exception("PETSc not initialized");
    456:       }
    457:       // Get a new cookie based on the class name
    458:       const char *id_name = ALE::getClassName<T>();
    459: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    460:       // Use id_name to register a cookie and events.
    461:       logged_allocator::_cookie = LogCookieRegister(id_name);
    462:       // Register the basic allocator methods' invocations as events; use the mangled class name.
    463:       logged_allocator::_allocate_event   = logged_allocator::__log_event_register(id_name, "allocate");
    464:       logged_allocator::_deallocate_event = logged_allocator::__log_event_register(id_name, "deallocate");
    465:       logged_allocator::_construct_event  = logged_allocator::__log_event_register(id_name, "construct");
    466:       logged_allocator::_destroy_event    = logged_allocator::__log_event_register(id_name, "destroy");
    467:       logged_allocator::_create_event     = logged_allocator::__log_event_register(id_name, "create");
    468:       logged_allocator::_del_event        = logged_allocator::__log_event_register(id_name, "del");
    469: #endif
    470:       ALE::restoreClassName<T>(id_name);
    471:       logged_allocator::_log_initialized = true;
    472:     }// if(!!logged_allocator::_log_initialized)
    473:   }// logged_allocator<T,O>::__log_initialize()

    475:   template <class T, bool O>
    476:   void logged_allocator<T, O>::__alloc_initialize() {
    477: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    478:     const char *id_name = ALE::getClassName<T>();
    479:     ALE::restoreClassName<T>(id_name);
    480: #endif
    481:   }// logged_allocator<T,O>::__alloc_initialize

    483:   template <class T, bool O>
    484:   void logged_allocator<T, O>::__alloc_finalize() {
    485: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    486: #endif
    487:   }// logged_allocator<T,O>::__alloc_finalize

    489:   template <class T, bool O>
    490:   LogEvent logged_allocator<T, O>::__log_event_register(const char *class_name, const char *event_name){
    491:     // This routine assumes a cookie has been obtained.
    492:     ostringstream txt;
    493:     if(O) {
    494:       txt << "Obj:";
    495:     }
    496: #ifdef ALE_LOGGING_VERBOSE
    497:     txt << class_name;
    498: #else
    499:     txt << "<allocator>";
    500: #endif
    501:     txt << ":" << event_name;
    502:     return LogEventRegister(logged_allocator::_cookie, txt.str().c_str());
    503:   }

    505:   template <class T, bool O>
    506:   T*  logged_allocator<T, O>::allocate(size_type _n) {
    507: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    508:     LogEventBegin(logged_allocator::_allocate_event);
    509: #endif
    510:     T* _p = polymorphic_allocator<T>::allocate(_n);
    511: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    512: //     PetscErrorCode PetscLogObjectMemory(this->_petscObj, _n*polymorphic_allocator<T>::sz);
    513: //     CHKERROR(ierr, "Error in PetscLogObjectMemory");
    514:     LogEventEnd(logged_allocator::_allocate_event);
    515: #endif
    516:     return _p;
    517:   }

    519:   template <class T, bool O>
    520:   void logged_allocator<T, O>::deallocate(T* _p, size_type _n) {
    521: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    522:     LogEventBegin(logged_allocator::_deallocate_event);
    523: #endif
    524:     polymorphic_allocator<T>::deallocate(_p, _n);
    525: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    526:     LogEventEnd(logged_allocator::_deallocate_event);
    527: #endif
    528:   }

    530:   template <class T, bool O>
    531:   void logged_allocator<T, O>::construct(T* _p, const T& _val) {
    532: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    533:     LogEventBegin(logged_allocator::_construct_event);
    534: #endif
    535:     polymorphic_allocator<T>::construct(_p, _val);
    536: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    537:     LogEventEnd(logged_allocator::_construct_event);
    538: #endif
    539:   }

    541:   template <class T, bool O>
    542:   void logged_allocator<T, O>::destroy(T* _p) {
    543: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    544:     LogEventBegin(logged_allocator::_destroy_event);
    545: #endif
    546:     polymorphic_allocator<T>::destroy(_p);
    547: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    548:     LogEventEnd(logged_allocator::_destroy_event);
    549: #endif
    550:   }

    552:   template <class T, bool O>
    553:   T* logged_allocator<T, O>::create(const T& _val) {
    554: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    555:     LogEventBegin(logged_allocator::_create_event);
    556: #endif
    557:     T* _p = polymorphic_allocator<T>::create(_val);
    558: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    559: //     PetscErrorCode PetscLogObjectMemory(this->_petscObj, polymorphic_allocator<T>::sz);
    560: //     CHKERROR(ierr, "Error in PetscLogObjectMemory");
    561:     LogEventEnd(logged_allocator::_create_event);
    562: #endif
    563:     return _p;
    564:   }

    566:   template <class T, bool O>
    567:   void logged_allocator<T, O>::del(T* _p) {
    568: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    569:     LogEventBegin(logged_allocator::_del_event);
    570: #endif
    571:     polymorphic_allocator<T>::del(_p);
    572: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    573:     LogEventEnd(logged_allocator::_del_event);
    574: #endif
    575:   }

    577:   template <class T, bool O> template <class TT>
    578:   void logged_allocator<T, O>::del(TT* _p, size_type _sz) {
    579: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    580:     LogEventBegin(logged_allocator::_del_event);
    581: #endif
    582:     polymorphic_allocator<T>::del(_p, _sz);
    583: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
    584:     LogEventEnd(logged_allocator::_del_event);
    585: #endif
    586:   }

    588: #ifdef ALE_USE_LOGGING
    589: #define ALE_ALLOCATOR ::ALE::logged_allocator
    590: #else
    591: #if 1
    592: #define ALE_ALLOCATOR ::ALE::malloc_allocator
    593: #else
    594: #define ALE_ALLOCATOR ::ALE::polymorphic_allocator
    595: #endif
    596: #endif

    598:   //
    599:   // The following classes define smart pointer behavior.
    600:   // They rely on allocators for memory pooling and logging (if logging is on).
    601:   //

    603:   // This is an Obj<X>-specific exception that is thrown when incompatible object conversion is attempted.
    604:   class BadCast : public Exception {
    605:   public:
    606:     explicit BadCast(const string&        msg) : Exception(msg) {};
    607:     explicit BadCast(const ostringstream& txt) : Exception(txt) {};
    608:     //  It actually looks like passing txt as an argument to Exception(ostringstream) performs a copy of txt,
    609:     //  which is disallowed due to the ostringstream constructor being private; must use a string constructor.
    610:     BadCast(const BadCast& e)        : Exception(e) {};
    611:   };

    613:   // This is the main smart pointer class.
    614:   template<class X, typename A = malloc_allocator<X> >
    615:   class Obj {
    616:   public:
    617:     // Types
    618: #if 1
    619:     typedef A                                               Allocator;
    620:     typedef typename Allocator::template rebind<int>::other Allocator_int;
    621: #else
    622: #ifdef ALE_USE_LOGGING
    623:     typedef logged_allocator<X,true>      Allocator;
    624:     typedef logged_allocator<int,true>    Allocator_int;
    625: #else
    626:     typedef polymorphic_allocator<X>      Allocator;
    627:     typedef polymorphic_allocator<int>    Allocator_int;
    628: #endif
    629: #endif
    630:     typedef typename Allocator::size_type size_type;
    631:   protected:
    632:     Allocator& allocator() {
    633:       static Allocator _allocator;

    635:       return _allocator;
    636:     };
    637:     Allocator_int& int_allocator() {
    638:       static Allocator_int _allocator;

    640:       return _allocator;
    641:     };
    642:   public:
    643:     X*        objPtr; // object pointer
    644:     int*      refCnt; // reference count
    645:     size_type sz;     // Size of underlying object (universal units) allocated with an allocator; indicates allocator use.
    646:     // Constructor; this can be made private, if we move operator Obj<Y> outside this class definition and make it a friend.
    647:     Obj(X *xx, int *refCnt, size_type sz);
    648:   public:
    649:     // Constructors & a destructor
    650:     Obj() : objPtr((X *)NULL), refCnt((int*)NULL), sz(0) {};
    651:     Obj(const X& x);
    652:     Obj(X *xx);
    653:     Obj(X *xx, size_type sz);
    654:     Obj(const Obj& obj);
    655:     virtual ~Obj();

    657:     // "Factory" methods
    658:     Obj& create(const X& x = X());
    659:     void destroy();

    661:     // predicates & assertions
    662:     bool isNull() const {return (this->objPtr == NULL);};
    663:     void assertNull(bool flag) const { if(this->isNull() != flag){ throw(Exception("Null assertion failed"));}};

    665:     // comparison operators
    666:     bool operator==(const Obj& obj) { return (this->objPtr == obj.objPtr);};
    667:     bool operator!=(const Obj& obj) { return (this->objPtr != obj.objPtr);};

    669:     // assignment/conversion operators
    670:     Obj& operator=(const Obj& obj);
    671:     template <class Y> operator Obj<Y> const();
    672:     template <class Y> Obj& operator=(const Obj<Y>& obj);

    674:     // dereference operators
    675:     X*   operator->() const {return objPtr;};

    677:     // "exposure" methods: expose the underlying object or object pointer
    678:     operator X*() {return objPtr;};
    679:     X& operator*() const {assertNull(false); return *objPtr;};
    680:     operator X()  {assertNull(false); return *objPtr;};
    681:     template<class Y> Obj& copy(const Obj<Y>& obj); // this operator will copy the underlying objects: USE WITH CAUTION

    684:     // depricated methods/operators
    685:     X* ptr() const     {return objPtr;};
    686:     X* pointer() const {return objPtr;};
    687:     X  obj() const     {assertNull(false); return *objPtr;};
    688:     X  object() const  {assertNull(false); return *objPtr;};

    690:     void addRef() {if (refCnt) {(*refCnt)++;}}
    691:   };// class Obj<X>

    693:   // Constructors
    694:   // New reference
    695:   template <class X, typename A>
    696:   Obj<X,A>::Obj(const X& x) {
    697:     this->refCnt = NULL;
    698:     this->create(x);
    699:   }

    701:   // Stolen reference
    702:   template <class X, typename A>
    703:   Obj<X,A>::Obj(X *xx){// such an object will be destroyed by calling 'delete' on its pointer
    704:                      // (e.g., we assume the pointer was obtained with new)
    705:     if (xx) {
    706:       this->objPtr = xx;
    707:       this->refCnt = int_allocator().create(1);
    708:       //this->refCnt   = new int(1);
    709:       this->sz = 0;
    710:     } else {
    711:       this->objPtr = NULL;
    712:       this->refCnt = NULL;
    713:       this->sz = 0;
    714:     }
    715:   }

    717:   // Work around for thing allocated with an allocator
    718:   template <class X, typename A>
    719:   Obj<X,A>::Obj(X *xx, size_type sz){// such an object will be destroyed by the allocator
    720:     if (xx) {
    721:       this->objPtr = xx;
    722:       this->refCnt = int_allocator().create(1);
    723:       this->sz     = sz;
    724:     } else {
    725:       this->objPtr = NULL;
    726:       this->refCnt = NULL;
    727:       this->sz = 0;
    728:     }
    729:   }

    731:   template <class X, typename A>
    732:   Obj<X,A>::Obj(X *_xx, int *_refCnt, size_type _sz) {  // This is intended to be private.
    733:     if (!_xx) {
    734:       throw ALE::Exception("Making an Obj with a NULL objPtr");
    735:     }
    736:     this->objPtr = _xx;
    737:     this->refCnt = _refCnt;  // we assume that all refCnt pointers are obtained using an int_allocator
    738:     (*this->refCnt)++;
    739:     this->sz = _sz;
    740:     //if (!this->sz) {
    741:     //  throw ALE::Exception("Making an Obj with zero size");
    742:     //}
    743:   }

    745:   template <class X, typename A>
    746:   Obj<X,A>::Obj(const Obj& obj) {
    747:     this->objPtr = obj.objPtr;
    748:     this->refCnt = obj.refCnt;
    749:     if (obj.refCnt) {
    750:       (*this->refCnt)++;
    751:     }
    752:     this->sz = obj.sz;
    753:     //if (!this->sz) {
    754:     //  throw ALE::Exception("Making an Obj with zero size");
    755:     //}
    756:   }

    758:   // Destructor
    759:   template <class X, typename A>
    760:   Obj<X,A>::~Obj(){
    761:     this->destroy();
    762:   }

    764:   template <class X, typename A>
    765:   Obj<X,A>& Obj<X,A>::create(const X& x) {
    766:     // Destroy the old state
    767:     this->destroy();
    768:     // Create the new state
    769:     this->objPtr = allocator().create(x);
    770:     this->refCnt = int_allocator().create(1);
    771:     this->sz     = allocator().sz;
    772:     if (!this->sz) {
    773:       throw ALE::Exception("Making an Obj with zero size obtained from allocator");
    774:     }
    775:     return *this;
    776:   }

    778:   template <class X, typename A>
    779:   void Obj<X,A>::destroy() {
    780:     if(ALE::getVerbosity() > 3) {
    781: #ifdef ALE_USE_DEBUGGING
    782:       const char *id_name = ALE::getClassName<X>();

    784:       printf("Obj<X>.destroy: Destroying Obj<%s>", id_name);
    785:       if (!this->refCnt) {
    786:         printf(" with no refCnt\n");
    787:       } else {
    788:         printf(" with refCnt %d\n", *this->refCnt);
    789:       }
    790:       ALE::restoreClassName<X>(id_name);
    791: #endif
    792:     }
    793:     if (this->refCnt != NULL) {
    794:       (*this->refCnt)--;
    795:       if (*this->refCnt == 0) {
    796:         // If  allocator has been used to create an objPtr, as indicated by 'sz', we use the allocator to delete objPtr, using 'sz'.
    797:         if(this->sz != 0) {
    798: #ifdef ALE_USE_DEBUGGING
    799:           if(ALE::getVerbosity() > 3) {
    800:             printf("  Calling deallocator on %p with size %d\n", this->objPtr, (int) this->sz);
    801:           }
    802: #endif
    803:           allocator().del(this->objPtr, this->sz);
    804:           this->sz = 0;
    805:         }
    806:         else { // otherwise we use 'delete'
    807: #ifdef ALE_USE_DEBUGGING
    808:           if(ALE::getVerbosity() > 3) {
    809:             printf("  Calling delete on %p\n", this->objPtr);
    810:           }
    811: #endif
    812:           if (!this->objPtr) {
    813:             throw ALE::Exception("Trying to free NULL pointer");
    814:           }
    815:           delete this->objPtr;
    816:         }
    817:         // refCnt is always created/delete using the int_allocator.
    818:         int_allocator().del(this->refCnt);
    819:         this->objPtr = NULL;
    820:         this->refCnt = NULL;
    821:       }
    822:     }
    823:   }

    825:   // assignment operator
    826:   template <class X, typename A>
    827:   Obj<X,A>& Obj<X,A>::operator=(const Obj<X,A>& obj) {
    828:     if(this->objPtr == obj.objPtr) {return *this;}
    829:     // Destroy 'this' Obj -- it will properly release the underlying object if the reference count is exhausted.
    830:     if(this->objPtr) {
    831:       this->destroy();
    832:     }
    833:     // Now copy the data from obj.
    834:     this->objPtr = obj.objPtr;
    835:     this->refCnt = obj.refCnt;
    836:     if(this->refCnt!= NULL) {
    837:       (*this->refCnt)++;
    838:     }
    839:     this->sz = obj.sz;
    840:     return *this;
    841:   }

    843:   // conversion operator, preserves 'this'
    844:   template<class X, typename A> template<class Y>
    845:   Obj<X,A>::operator Obj<Y> const() {
    846:     // We attempt to cast X* objPtr to Y* using dynamic_
    847: #ifdef ALE_USE_DEBUGGING
    848:     if(ALE::getVerbosity() > 1) {
    849:       printf("Obj<X>::operator Obj<Y>: attempting a dynamic_cast on objPtr %p\n", this->objPtr);
    850:     }
    851: #endif
    852:     Y* yObjPtr = dynamic_cast<Y*>(this->objPtr);
    853:     // If the cast failed, throw an exception
    854:     if(yObjPtr == NULL) {
    855:       const char *Xname = ALE::getClassName<X>();
    856:       const char *Yname = ALE::getClassName<Y>();
    857:       std::string msg("Bad cast Obj<");
    858:       msg += Xname;
    859:       msg += "> --> Obj<";
    860:       msg += Yname;
    861:       msg += ">";
    862:       ALE::restoreClassName<X>(Xname);
    863:       ALE::restoreClassName<X>(Yname);
    864:       throw BadCast(msg.c_str());
    865:     }
    866:     // Okay, we can proceed
    867:     return Obj<Y>(yObjPtr, this->refCnt, this->sz);
    868:   }

    870:   // assignment-conversion operator
    871:   template<class X, typename A> template<class Y>
    872:   Obj<X,A>& Obj<X,A>::operator=(const Obj<Y>& obj) {
    873:     // We attempt to cast Y* obj.objPtr to X* using dynamic_cast
    874:     X* xObjPtr = dynamic_cast<X*>(obj.objPtr);
    875:     // If the cast failed, throw an exception
    876:     if(xObjPtr == NULL) {
    877:       const char *Xname = ALE::getClassName<X>();
    878:       const char *Yname = ALE::getClassName<Y>();
    879:       std::string msg("Bad assignment cast Obj<");
    880:       msg += Yname;
    881:       msg += "> --> Obj<";
    882:       msg += Xname;
    883:       msg += ">";
    884:       ALE::restoreClassName<X>(Xname);
    885:       ALE::restoreClassName<X>(Yname);
    886:       throw BadCast(msg.c_str());
    887:     }
    888:     // Okay, we can proceed with the assignment
    889:     if(this->objPtr == obj.objPtr) {return *this;}
    890:     // Destroy 'this' Obj -- it will properly release the underlying object if the reference count is exhausted.
    891:     this->destroy();
    892:     // Now copy the data from obj.
    893:     this->objPtr = xObjPtr;
    894:     this->refCnt = obj.refCnt;
    895:     (*this->refCnt)++;
    896:     this->sz = obj.sz;
    897:     return *this;
    898:   }

    900:   // copy operator (USE WITH CAUTION)
    901:   template<class X, typename A> template<class Y>
    902:   Obj<X,A>& Obj<X,A>::copy(const Obj<Y>& obj) {
    903:     if(this->isNull() || obj.isNull()) {
    904:       throw(Exception("Copying to or from a null Obj"));
    905:     }
    906:     *(this->objPtr) = *(obj.objPtr);
    907:     return *this;
    908:   }

    911: } // namespace ALE

    913: #endif