static_memory_gremlin.cpp

Go to the documentation of this file.
00001 #ifndef STATIC_MEMORY_GREMLIN_IMPLEMENTATION_FILE
00002 #define STATIC_MEMORY_GREMLIN_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : static_memory_gremlin                                             *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 2004-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "static_memory_gremlin.h"
00019 
00020 #include <basis/array.cpp>
00021 #include <basis/byte_array.h>
00022 #include <basis/function.h>
00023 #include <basis/guards.h>
00024 #include <basis/mutex.h>
00025 #include <basis/object_base.h>
00026 #include <basis/set.cpp>
00027 
00028 #include <stdio.h>
00029 
00030 // these two are hierarchy violations that are allowed.  otherwise any
00031 // SET_DEFAULT_X logger macro is going to be unavailable here in data_struct.
00032 #include <loggers/file_logger.h>
00033 #include <loggers/locked_logger.h>
00034 #include <loggers/null_logger.h>
00035 
00036 #define static_class_name() "static_memory_gremlin"
00037 
00038 //#define DEBUG_STATIC_MEMORY_GREMLIN
00039   // comment this out to eliminate debugging print-outs.  otherwise they
00040   // are controlled by the class interface.
00041 
00042 //#define SKIP_STATIC_CLEANUP
00043   // don't uncomment this unless you want all static objects to be left
00044   // allocated on shutdown of the program.  that's very sloppy but may
00045   // sometimes be needed for testing.
00046 
00047 const char *_EXCEPTION_INFO_FOR_SHUTTING_DOWN
00048     = "Function must return immediately because program is shutting down.";
00049 
00050 static bool _program_is_dying = false;
00051   // this is set to true when no more logging or access to static objects
00052   // should be allowed.
00053 
00054 bool DATA_STRUCTURE_FUNCTION_STYLE program_is_dying() { return _program_is_dying; }
00055 
00056 const char DATA_STRUCTURE_FUNCTION_STYLE *EXCEPTION_INFO_FOR_SHUTTING_DOWN()
00057 { return _EXCEPTION_INFO_FOR_SHUTTING_DOWN; }
00058 
00059 const byte_array BASIS_EXTERN &__byte_array_empty_array()
00060 {
00061   static byte_array __hidden_byte_array;
00062   return __hidden_byte_array;
00063 }
00064 
00065 const istring BASIS_EXTERN &__istring_empty_string()
00066 {
00067   static istring __hidden_empty_string;
00068   return __hidden_empty_string;
00069 }
00070 
00072 
00073 const int SMG_CHUNKING_FACTOR = 32;
00074   // we'll allocate this many indices at a time.
00075 
00076 class object_base_record
00077 {
00078 public:
00079   object_base *_object;
00080   const char *_name;
00081 };
00082 
00083 static_memory_gremlin::static_memory_gremlin()
00084 : _lock(new mutex),
00085   _top_index(0),
00086   _actual_size(0),
00087   _pointers_held(NIL),
00088   _show_debugging(false)
00089 {
00090   ensure_space_exists();
00091 }
00092 
00093 static_memory_gremlin::~static_memory_gremlin()
00094 {
00095   _program_is_dying = true;
00096     // now the rest of the program is on notice; we're practically gone.
00097 
00098   if (!program_is_dying()) {
00099     non_continuable_error("static_memory_gremlin", "destructor",
00100         "program is not in dying state; this is a grievous error.");
00101   }
00102 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
00103   if (_show_debugging)
00104     printf("SMG: beginning static object shutdown...\n");
00105 #endif
00106 
00107 #ifndef SKIP_STATIC_CLEANUP
00108   // clean up any allocated pointers in reverse order of addition.
00109   while (_top_index > 0) {
00110     // make sure we fixate on which guy is shutting down.  some new ones
00111     // could be added on the end of the list as a result of this destruction.
00112     int zapped_index = _top_index - 1;
00113     object_base_record *ptr = _pointers_held[zapped_index];
00114     _pointers_held[zapped_index] = NIL;
00115       // since we know the one we're zapping, we no longer need that index.
00116     _top_index--;
00117       // this should allow us to keep chewing on items that are newly being
00118       // added during static shutdown, since we have taken the current object
00119       // entirely out of the picture.
00120     if (ptr) {
00121 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
00122       if (_show_debugging)
00123         printf((istring("SMG: deleting ") + ptr->_object->instance_name()
00124             + " called " + ptr->_name
00125             + isprintf(" at index %d.\n", zapped_index) ).s());
00126 #endif
00127       WHACK(ptr->_object);
00128       WHACK(ptr);
00129     }
00130   }
00131 #endif
00132   delete [] _pointers_held;
00133   _pointers_held = NIL;
00134   delete _lock;
00135   _lock = NIL;
00136 }
00137 
00138 int static_memory_gremlin::locate(const char *unique_name)
00139 {
00140   auto_synchronizer l(*_lock);
00141   for (int i = 0; i < _top_index; i++) {
00142     if (!strcmp(_pointers_held[i]->_name, unique_name)) return i;
00143   }
00144   return common::NOT_FOUND;
00145 }
00146 
00147 object_base *static_memory_gremlin::get(const char *unique_name)
00148 {
00149   auto_synchronizer l(*_lock);
00150   int indy = locate(unique_name);
00151   if (negative(indy)) return NIL;
00152   return _pointers_held[indy]->_object;
00153 }
00154 
00155 const char *static_memory_gremlin::find(const object_base *ptr)
00156 {
00157   auto_synchronizer l(*_lock);
00158   for (int i = 0; i < _top_index; i++) {
00159     if (ptr == _pointers_held[i]->_object)
00160       return _pointers_held[i]->_name;
00161   }
00162   return NIL;
00163 }
00164 
00165 bool static_memory_gremlin::put(const char *unique_name, object_base *to_put)
00166 {
00167   auto_synchronizer l(*_lock);
00168   int indy = locate(unique_name);
00169   // see if that name already exists.
00170   if (non_negative(indy)) {
00171 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
00172     if (_show_debugging)
00173       printf((istring("SMG: cleaning out old object ")
00174           + _pointers_held[indy]->_object->instance_name()
00175           + " called " + _pointers_held[indy]->_name 
00176           + " in favor of object " + to_put->instance_name()
00177           + " called " + unique_name
00178           + isprintf(" at index %d.\n", indy)).s());
00179 #endif
00180     WHACK(_pointers_held[indy]->_object);
00181     _pointers_held[indy]->_object = to_put;
00182     return true;
00183   }
00184 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
00185   if (_show_debugging)
00186     printf((istring("SMG: storing ") + to_put->instance_name()
00187         + " called " + unique_name
00188         + isprintf(" at index %d.\n", _top_index)).s());
00189 #endif
00190   ensure_space_exists();
00191   _pointers_held[_top_index] = new object_base_record;
00192   _pointers_held[_top_index]->_object = to_put;
00193   _pointers_held[_top_index]->_name = unique_name;
00194   _top_index++;
00195   return true;
00196 }
00197 
00198 void static_memory_gremlin::ensure_space_exists()
00199 {
00200   auto_synchronizer l(*_lock);
00201   if (!_pointers_held || (_top_index + 1 >= _actual_size) ) {
00202     // never had any contents yet or not enough space exists.
00203 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
00204     if (_show_debugging)
00205       printf(isprintf("SMG: adding space for top at %d.\n", _top_index).s());
00206 #endif
00207     _actual_size += SMG_CHUNKING_FACTOR;
00208     typedef object_base_record *base_ptr;
00209     object_base_record **new_ptr = new base_ptr[_actual_size];
00210     if (!new_ptr) {
00211       non_continuable_error("static_memory_gremlin", "ensure_space_exists",
00212           "failed to allocate memory for pointer list");
00213     }
00214     for (int i = 0; i < _actual_size; i++) new_ptr[i] = NIL;
00215     for (int j = 0; j < _actual_size - SMG_CHUNKING_FACTOR; j++) 
00216       new_ptr[j] = _pointers_held[j];
00217     if (_pointers_held) delete [] _pointers_held;
00218     _pointers_held = new_ptr;
00219   }
00220 }
00221 
00222 // this function ensures that the space for the global objects is kept until
00223 // the program goes away.  if it's the first time through, then the gremlin
00224 // gets created; otherwise the existing one is used.  this function should
00225 // always be called by the main program before any attempts to use global
00226 // features like SAFE_STATIC or the program wide logger.  it is crucial that no
00227 // user-level threads have been created in the program before this is called.
00228 static_memory_gremlin &HOOPLE_GLOBALS()
00229 {
00230   static bool _initted = false;  // tells whether we've gone through yet.
00231   static static_memory_gremlin *_internal_gremlin = NIL;
00232     // holds our list of shared pieces...
00233 
00234   if (!_initted) {
00235 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
00236     printf("%s: initializing HOOPLE_GLOBALS now.\n", __argv[0]); 
00237 #endif
00238 
00239 #ifdef ENABLE_MEMORY_HOOK
00240     void *temp = program_wide_memories().provide_memory(1, __FILE__, __LINE__);
00241       // invoke now to get memory engine instantiated.
00242     program_wide_memories().release_memory(temp);  // clean up junk.
00243 #endif
00244 
00245 #ifdef ENABLE_CALLSTACK_TRACKING
00246     program_wide_stack_trace().full_trace_size();
00247       // invoke now to get callback tracking instantiated.
00248 #endif
00249     FUNCDEF("HOOPLE_GLOBALS remainder");
00250       // this definition must be postponed until after the objects that would
00251       // track it actually exist.
00252 
00253     // this simple approach is not going to succeed if the SAFE_STATIC macros
00254     // are used in a static library which is then used in more than one dynamic
00255     // library on win32.  this is because each dll in win32 will have a
00256     // different version of certain static objects that should only occur once
00257     // per program.  this problem is due to the win32 memory model, but in
00258     // hoople at least this has been prevented; our only static library that
00259     // appears in a bunch of dlls is basis and it is not allowed to use the
00260     // SAFE_STATIC macro.
00261     _internal_gremlin = new static_memory_gremlin;
00262     _initted = true;
00263   }
00264 
00265   return *_internal_gremlin;
00266 }
00267 
00269 
00270 //hmmm: chunk to move elsewhere.
00271 
00272 #ifdef ENABLE_MEMORY_HOOK
00273 
00274 static memory_checker *__exposed_hidden_memories = NIL;
00275   // set once the real memory object is created below.  not to be used by
00276   // anyone else besides the atexit method.
00277 
00278 void hoople_atexit()
00279 {
00280   if (__exposed_hidden_memories) {
00281     __exposed_hidden_memories->destruct();
00282       // this renders the object useless but leaves it able to alloc/dealloc.
00283     __exposed_hidden_memories = NIL;
00284   }
00285 }
00286 
00288 
00294 memory_checker BASIS_EXTERN &program_wide_memories()
00295 {
00296   static memory_checker *_hidden_memories = NIL;
00297   if (!_hidden_memories) {
00298     static bool _already_here = false;
00299     if (_already_here) {
00300       printf("logic error in program_wide_memories: invoked after shutdown\n");
00301       abort();
00302     }
00303     _already_here = true;
00304 
00305     _hidden_memories = (memory_checker *)malloc(sizeof(memory_checker));
00306     _hidden_memories->construct();
00307     // we make an external copy of our internal object here.
00308     __exposed_hidden_memories = _hidden_memories;
00309     // register a program shutdown handler to report leaks.
00310     atexit(hoople_atexit); 
00311   }
00312   return *_hidden_memories;
00313     // there will be at least one reported memory leak if this program is
00314     // analyzed with external tools; the hidden memories pointer is never
00315     // deallocated here.
00316 }
00317 
00318 #endif
00319 
00321 
00322 //hmmm: chunk to move elsewhere.
00323 
00324 #ifdef ENABLE_CALLSTACK_TRACKING
00325 
00327 
00334 callstack_tracker BASIS_EXTERN &program_wide_stack_trace()
00335 {
00336   static callstack_tracker *_hidden_trace = NIL;
00337   if (!_hidden_trace) {
00338 #ifdef ENABLE_MEMORY_HOOK
00339     program_wide_memories().disable();
00340       // we don't want infinite loops tracking the call stack during this
00341       // object's construction.
00342 #endif
00343     _hidden_trace = new callstack_tracker;
00344 #ifdef ENABLE_MEMORY_HOOK
00345     program_wide_memories().enable();
00346 #endif
00347   }
00348   return *_hidden_trace;
00349 }
00350 
00351 #endif
00352 
00354 
00355 library_wide_cleanup::library_wide_cleanup()
00356 : _shared_chunks(&HOOPLE_GLOBALS())  // create the memory chunk if needed.
00357 {
00358   __memory_gremlin_synchronizer();
00359     // invoke this at least once; this is our most critical synchro primitive.
00360 
00361   // these force static objects in the statically linked basis library to be
00362   // created properly on win32.  we want to ensure that the wacky memory
00363   // context of a static library's statics are set up properly for use in each
00364   // dynamic library, since there seem to be a separate static heap context in
00365   // each DLL.  that poses a problem in general for more complex objects if
00366   // they are contained in a widely-used static library.  basis is the only
00367   // real case of that in our system, and it's been reduced down to just the
00368   // following statics (which are wrapped in creator methods).
00369 //hmmm: get these back to being exported from opsys instead; singletons.
00370   istring::empty_string();
00371   byte_array::empty_array();
00372 }
00373 
00374 library_wide_cleanup::~library_wide_cleanup() {}
00375   // nothing here yet.
00376 
00378 
00379 program_wide_cleanup::program_wide_cleanup()
00380 {
00381   // reset the random number seed.  we only do this once during startup
00382   // although anyone can reset it again later.
00383   chaos().retrain();
00384 #ifndef OMIT_PROGRAM_WIDE_LOGGER
00385   // assume we're okay by creating a file + console logger until they decide
00386   // to set up a different type.
00387   SET_DEFAULT_COMBO_LOGGER;
00388 #endif
00389 }
00390 
00391 program_wide_cleanup::~program_wide_cleanup()
00392 {
00393 #ifndef OMIT_PROGRAM_WIDE_LOGGER
00394   if (real_program_wide_logger().established()) {
00395     SET_DEFAULT_NULL_LOGGER;
00396       // reset to a non-asynchronous logger.  if a logger that's busy in the
00397       // background (like the uls) is hooked in here, it could still be trying
00398       // to deliver sends while the program is trying to shut down.  we just
00399       // ensure it gets closed before much more of static shutdown happens.
00400   }
00401 #endif
00402 
00403   // remove our shared segments.
00404   static_memory_gremlin *shared = &HOOPLE_GLOBALS();
00405 
00406   WHACK(shared);  // now really remove the object.
00407 }
00408 
00410 
00411 // internal secret below: we are always planning on this function being called
00412 // before any other functions that use it, and this object should always exist
00413 // for program-wide synchronization needs, so we just assume it is safe to
00414 // always take the zeroth identifier/slot from the gremlin.  we do this
00415 // because the SAFE_STATIC macros actually use the private synchronizer.
00416 mutex &__memory_gremlin_synchronizer()
00417 {
00418   const char *hoople_static_name
00419       = ":" __FILE__ ":" "only___memory_gremlin_synch" ":";
00420   static mutex *_hidden_mutt = dynamic_cast<mutex *>
00421       (HOOPLE_GLOBALS().get(hoople_static_name));
00422   if (!_hidden_mutt) {
00423     _hidden_mutt = new mutex;
00424     HOOPLE_GLOBALS().put(hoople_static_name, _hidden_mutt);
00425   }
00426   return *_hidden_mutt;
00427 }
00428 
00429 int_set BASIS_EXTERN &__our_kids()
00430 { SAFE_STATIC_IMPLEMENTATION(int_set, , __LINE__); }
00431 
00432 mutex BASIS_EXTERN &__uptime_synchronizer()
00433 { SAFE_STATIC_IMPLEMENTATION(mutex, , __LINE__); }
00434 
00435 mutex BASIS_EXTERN &__process_synchronizer()
00436 { SAFE_STATIC_IMPLEMENTATION(mutex, , __LINE__); }
00437 
00439 
00440 #undef static_class_name
00441 
00442 #endif //STATIC_MEMORY_GREMLIN_IMPLEMENTATION_FILE

Generated on Fri Sep 5 04:28:42 2008 for HOOPLE Libraries by  doxygen 1.5.1