Actual source code: ALE_mem.hh

petsc-3.3-p7 2013-05-11
  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;};
343: 
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);
446: 
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:   }
518: 
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:   }
529: 
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:   }
540: 
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:   }
551: 
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);};
668: 
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;};
676: 
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
682: 

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:   }
700: 
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:   }
730: 
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:   }
744: 
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:   }
899: 
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