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