Actual source code: ALE_mem.hh

  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 <ALE_log.hh>

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

 19: namespace ALE {
 20:   class MemoryLogger;
 21: }

 24: namespace ALE {
 25:   class MemoryLogger {
 26:   public:
 27:     struct Log {
 28:       int num;
 29:       int total;
 30:       std::map<std::string, int> 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("Cannot push duplicate stage name");
 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:     int getNumAllocations() {return getNumAllocations(stageNames.front());};
109:     int getNumAllocations(const std::string& stage) {return stages[stage].first.num;};
110:     int getNumDeallocations() {return getNumDeallocations(stageNames.front());};
111:     int getNumDeallocations(const std::string& stage) {return stages[stage].second.num;};
112:     int getAllocationTotal() {return getAllocationTotal(stageNames.front());};
113:     int getAllocationTotal(const std::string& stage) {return stages[stage].first.total;};
114:     int getDeallocationTotal() {return getDeallocationTotal(stageNames.front());};
115:     int 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:         std::cout << "["<<rank<<"]  " << s_iter->first << ": " << s_iter->second.second.num << " dcalls  " << s_iter->second.second.total << " bytes" << std::endl;
122:       }
123:     };
124:   public:
125:     template<typename T>
126:     static const char *getClassName() {
127:       const std::type_info& id      = typeid(T);
128:       char                 *id_name = const_cast<char *>(id.name());

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

136:       if (!status) {
137:         id_name = id_name_demangled;
138:       }
139: #endif
140:       return id_name;
141:     }
142:     static void restoreClassName(const char * /* className */) {};
143:   };

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

183:     pointer allocate(size_type n, const_pointer = 0) {
184:       assert(n >= 0);
185: #ifdef ALE_MEM_LOGGING
186:       ALE::MemoryLogger::singleton().logAllocation(className, n * sizeof(T));
187: #endif
188:       numAllocs++;
189:       void *p = std::malloc(n * sizeof(T));
190:       if (!p) throw std::bad_alloc();
191:       return static_cast<pointer>(p);
192:     }

194: #ifdef ALE_MEM_LOGGING
195:     void deallocate(pointer p, size_type n) {
196:       ALE::MemoryLogger::singleton().logDeallocation(className, n * sizeof(T));
197:       std::free(p);
198:     }
199: #else
200:     void deallocate(pointer p, size_type) {
201:       std::free(p);
202:     }
203: #endif

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

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

209:     void destroy(pointer p) {p->~value_type();}
210:   public:
211:     pointer create(const value_type& x = value_type()) {
212:       pointer p = (pointer) allocate(1);
213:       construct(p, x);
214:       return p;
215:     };

217:     void del(pointer p) {
218:       destroy(p);
219:       deallocate(p, 1);
220:     };

222:     // This is just to be compatible with Dmitry's weird crap for now
223:     void del(pointer p, size_type size) {
224:       if (size != sizeof(value_type)) throw std::exception();
225:       destroy(p);
226:       deallocate(p, 1);
227:     };
228:   private:
229:     void operator=(const malloc_allocator&);
230:   };

232:   template<> class malloc_allocator<void>
233:   {
234:     typedef void        value_type;
235:     typedef void*       pointer;
236:     typedef const void* const_pointer;

238:     template <class U>
239:     struct rebind {typedef malloc_allocator<U> other;};
240:   };

242:   template <class T>
243:   inline bool operator==(const malloc_allocator<T>&, const malloc_allocator<T>&) {
244:     return true;
245:   };

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

252:   template <class T>
253:   static const char *getClassName() {
254:     const std::type_info& id = typeid(T);
255:     const char *id_name;

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

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

290:   // This UNIVERSAL allocator class is static and provides allocation/deallocation services to all allocators defined below.
291:   class universal_allocator {
292:   public:
293:     typedef std::size_t size_type;
294:     static char*     allocate(const size_type& sz);
295:     static void      deallocate(char *p, const size_type& sz);
296:     static size_type max_size();
297:   };

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

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

320:     polymorphic_allocator()                                    {};
321:     polymorphic_allocator(const polymorphic_allocator& a)      {};
322:     template <class TT>
323:     polymorphic_allocator(const polymorphic_allocator<TT>& aa){}
324:     ~polymorphic_allocator() {};

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

343:   template <class T>
344:   typename polymorphic_allocator<T>::Alloc polymorphic_allocator<T>::alloc;

346:   //IMPORTANT: allocator 'sz' calculation takes place here
347:   template <class T>
348:   typename polymorphic_allocator<T>::size_type polymorphic_allocator<T>::sz =
349:     (typename polymorphic_allocator<T>::size_type)(ceil(sizeof(T)/sizeof(char)));

351:   template <class T>
352:   T* polymorphic_allocator<T>::create(const T& _val) {
353:     // First, allocate space for a single object
354:     T* _p = (T*)universal_allocator::allocate(sz);
355:     // Construct an object in the provided space using the provided initial value
356:     this->alloc.construct(_p,  _val);
357:     return _p;
358:   }

360:   template <class T>
361:   void polymorphic_allocator<T>::del(T* _p) {
362:     _p->~T();
363:     universal_allocator::deallocate((char*)_p, polymorphic_allocator<T>::sz);
364:   }

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


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

414:     T*   allocate(size_type _n);
415:     void deallocate(T*  _p, size_type _n);
416:     void construct(T* _p, const T& _val);
417:     void destroy(T* _p);

419:     T*  create(const T& _val = T());
420:     void del(T*  _p);
421:     template <class TT> void del(TT* _p, size_type _sz);
422:   };

424:   template <class T, bool O>
425:   bool logged_allocator<T, O>::_log_initialized(false);
426:   template <class T, bool O>
427:   LogCookie logged_allocator<T,O>::_cookie(0);
428:   template <class T, bool O>
429:   int logged_allocator<T, O>::_allocate_event(0);
430:   template <class T, bool O>
431:   int logged_allocator<T, O>::_deallocate_event(0);
432:   template <class T, bool O>
433:   int logged_allocator<T, O>::_construct_event(0);
434:   template <class T, bool O>
435:   int logged_allocator<T, O>::_destroy_event(0);
436:   template <class T, bool O>
437:   int logged_allocator<T, O>::_create_event(0);
438:   template <class T, bool O>
439:   int logged_allocator<T, O>::_del_event(0);
440: 
441:   template <class T, bool O>
442:   void logged_allocator<T, O>::__log_initialize() {
443:     if(!logged_allocator::_log_initialized) {
444:       // First of all we make sure PETSc is initialized
445:       PetscTruth     flag;
446:       PetscErrorCode PetscInitialized(&flag);CHKERROR(ierr, "Error in PetscInitialized");
447:       if(!flag) {
448:         // I guess it would be nice to initialize PETSc here, but we'd need argv/argc here
449:         throw ALE::Exception("PETSc not initialized");
450:       }
451:       // Get a new cookie based on the class name
452:       const char *id_name = ALE::getClassName<T>();
453: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
454:       // Use id_name to register a cookie and events.
455:       logged_allocator::_cookie = LogCookieRegister(id_name);
456:       // Register the basic allocator methods' invocations as events; use the mangled class name.
457:       logged_allocator::_allocate_event   = logged_allocator::__log_event_register(id_name, "allocate");
458:       logged_allocator::_deallocate_event = logged_allocator::__log_event_register(id_name, "deallocate");
459:       logged_allocator::_construct_event  = logged_allocator::__log_event_register(id_name, "construct");
460:       logged_allocator::_destroy_event    = logged_allocator::__log_event_register(id_name, "destroy");
461:       logged_allocator::_create_event     = logged_allocator::__log_event_register(id_name, "create");
462:       logged_allocator::_del_event        = logged_allocator::__log_event_register(id_name, "del");
463: #endif
464:       ALE::restoreClassName<T>(id_name);
465:       logged_allocator::_log_initialized = true;
466:     }// if(!!logged_allocator::_log_initialized)
467:   }// logged_allocator<T,O>::__log_initialize()

469:   template <class T, bool O>
470:   void logged_allocator<T, O>::__alloc_initialize() {
471: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
472:     const char *id_name = ALE::getClassName<T>();
473:     ALE::restoreClassName<T>(id_name);
474: #endif
475:   }// logged_allocator<T,O>::__alloc_initialize

477:   template <class T, bool O>
478:   void logged_allocator<T, O>::__alloc_finalize() {
479: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
480: #endif
481:   }// logged_allocator<T,O>::__alloc_finalize

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

499:   template <class T, bool O>
500:   T*  logged_allocator<T, O>::allocate(size_type _n) {
501: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
502:     LogEventBegin(logged_allocator::_allocate_event);
503: #endif
504:     T* _p = polymorphic_allocator<T>::allocate(_n);
505: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
506: //     PetscErrorCode PetscLogObjectMemory(this->_petscObj, _n*polymorphic_allocator<T>::sz);
507: //     CHKERROR(ierr, "Error in PetscLogObjectMemory");
508:     LogEventEnd(logged_allocator::_allocate_event);
509: #endif
510:     return _p;
511:   }
512: 
513:   template <class T, bool O>
514:   void logged_allocator<T, O>::deallocate(T* _p, size_type _n) {
515: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
516:     LogEventBegin(logged_allocator::_deallocate_event);
517: #endif
518:     polymorphic_allocator<T>::deallocate(_p, _n);
519: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
520:     LogEventEnd(logged_allocator::_deallocate_event);
521: #endif
522:   }
523: 
524:   template <class T, bool O>
525:   void logged_allocator<T, O>::construct(T* _p, const T& _val) {
526: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
527:     LogEventBegin(logged_allocator::_construct_event);
528: #endif
529:     polymorphic_allocator<T>::construct(_p, _val);
530: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
531:     LogEventEnd(logged_allocator::_construct_event);
532: #endif
533:   }
534: 
535:   template <class T, bool O>
536:   void logged_allocator<T, O>::destroy(T* _p) {
537: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
538:     LogEventBegin(logged_allocator::_destroy_event);
539: #endif
540:     polymorphic_allocator<T>::destroy(_p);
541: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
542:     LogEventEnd(logged_allocator::_destroy_event);
543: #endif
544:   }
545: 
546:   template <class T, bool O>
547:   T* logged_allocator<T, O>::create(const T& _val) {
548: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
549:     LogEventBegin(logged_allocator::_create_event);
550: #endif
551:     T* _p = polymorphic_allocator<T>::create(_val);
552: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
553: //     PetscErrorCode PetscLogObjectMemory(this->_petscObj, polymorphic_allocator<T>::sz);
554: //     CHKERROR(ierr, "Error in PetscLogObjectMemory");
555:     LogEventEnd(logged_allocator::_create_event);
556: #endif
557:     return _p;
558:   }

560:   template <class T, bool O>
561:   void logged_allocator<T, O>::del(T* _p) {
562: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
563:     LogEventBegin(logged_allocator::_del_event);
564: #endif
565:     polymorphic_allocator<T>::del(_p);
566: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
567:     LogEventEnd(logged_allocator::_del_event);
568: #endif
569:   }

571:   template <class T, bool O> template <class TT>
572:   void logged_allocator<T, O>::del(TT* _p, size_type _sz) {
573: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
574:     LogEventBegin(logged_allocator::_del_event);
575: #endif
576:     polymorphic_allocator<T>::del(_p, _sz);
577: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
578:     LogEventEnd(logged_allocator::_del_event);
579: #endif
580:   }

582: #ifdef ALE_USE_LOGGING
583: #define ALE_ALLOCATOR ::ALE::logged_allocator
584: #else
585: #if 1
586: #define ALE_ALLOCATOR ::ALE::malloc_allocator
587: #else
588: #define ALE_ALLOCATOR ::ALE::polymorphic_allocator
589: #endif
590: #endif

592:   //
593:   // The following classes define smart pointer behavior.
594:   // They rely on allocators for memory pooling and logging (if logging is on).
595:   //

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

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

629:       return _allocator;
630:     };
631:     Allocator_int& int_allocator() {
632:       static Allocator_int _allocator;

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

651:     // "Factory" methods
652:     Obj& create(const X& x = X());
653:     void destroy();

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

659:     // comparison operators
660:     bool operator==(const Obj& obj) { return (this->objPtr == obj.objPtr);};
661:     bool operator!=(const Obj& obj) { return (this->objPtr != obj.objPtr);};
662: 
663:     // assignment/conversion operators
664:     Obj& operator=(const Obj& obj);
665:     template <class Y> operator Obj<Y> const();
666:     template <class Y> Obj& operator=(const Obj<Y>& obj);

668:     // dereference operators
669:     X*   operator->() const {return objPtr;};
670: 
671:     // "exposure" methods: expose the underlying object or object pointer
672:     operator X*() {return objPtr;};
673:     X& operator*() const {assertNull(false); return *objPtr;};
674:     operator X()  {assertNull(false); return *objPtr;};
675:     template<class Y> Obj& copy(const Obj<Y>& obj); // this operator will copy the underlying objects: USE WITH CAUTION
676: 

678:     // depricated methods/operators
679:     X* ptr() const     {return objPtr;};
680:     X* pointer() const {return objPtr;};
681:     X  obj() const     {assertNull(false); return *objPtr;};
682:     X  object() const  {assertNull(false); return *objPtr;};

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

687:   // Constructors
688:   // New reference
689:   template <class X, typename A>
690:   Obj<X,A>::Obj(const X& x) {
691:     this->refCnt = NULL;
692:     this->create(x);
693:   }
694: 
695:   // Stolen reference
696:   template <class X, typename A>
697:   Obj<X,A>::Obj(X *xx){// such an object will be destroyed by calling 'delete' on its pointer
698:                      // (e.g., we assume the pointer was obtained with new)
699:     if (xx) {
700:       this->objPtr = xx;
701:       this->refCnt = int_allocator().create(1);
702:       //this->refCnt   = new int(1);
703:       this->sz = 0;
704:     } else {
705:       this->objPtr = NULL;
706:       this->refCnt = NULL;
707:       this->sz = 0;
708:     }
709:   }

711:   // Work around for thing allocated with an allocator
712:   template <class X, typename A>
713:   Obj<X,A>::Obj(X *xx, size_type sz){// such an object will be destroyed by the allocator
714:     if (xx) {
715:       this->objPtr = xx;
716:       this->refCnt = int_allocator().create(1);
717:       this->sz     = sz;
718:     } else {
719:       this->objPtr = NULL;
720:       this->refCnt = NULL;
721:       this->sz = 0;
722:     }
723:   }
724: 
725:   template <class X, typename A>
726:   Obj<X,A>::Obj(X *_xx, int *_refCnt, size_type _sz) {  // This is intended to be private.
727:     if (!_xx) {
728:       throw ALE::Exception("Making an Obj with a NULL objPtr");
729:     }
730:     this->objPtr = _xx;
731:     this->refCnt = _refCnt;  // we assume that all refCnt pointers are obtained using an int_allocator
732:     (*this->refCnt)++;
733:     this->sz = _sz;
734:     //if (!this->sz) {
735:     //  throw ALE::Exception("Making an Obj with zero size");
736:     //}
737:   }
738: 
739:   template <class X, typename A>
740:   Obj<X,A>::Obj(const Obj& obj) {
741:     this->objPtr = obj.objPtr;
742:     this->refCnt = obj.refCnt;
743:     if (obj.refCnt) {
744:       (*this->refCnt)++;
745:     }
746:     this->sz = obj.sz;
747:     //if (!this->sz) {
748:     //  throw ALE::Exception("Making an Obj with zero size");
749:     //}
750:   }

752:   // Destructor
753:   template <class X, typename A>
754:   Obj<X,A>::~Obj(){
755:     this->destroy();
756:   }

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

772:   template <class X, typename A>
773:   void Obj<X,A>::destroy() {
774:     if(ALE::getVerbosity() > 3) {
775: #ifdef ALE_USE_DEBUGGING
776:       const char *id_name = ALE::getClassName<X>();

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

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

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

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


905: } // namespace ALE

907: #endif