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