Merge branch 'main' of feistymeow.org:feisty_meow
[feisty_meow.git] / structures / static_memory_gremlin.cpp
1 /*
2 *  Name   : static_memory_gremlin
3 *  Author : Chris Koeritz
4 *
5 * Copyright (c) 2004-$now By Author.  This program is free software; you can
6 * redistribute it and/or modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either version 2 of
8 * the License or (at your option) any later version.  This is online at:
9 *     http://www.fsf.org/copyleft/gpl.html
10 * Please send any updates to: fred@gruntose.com
11 */
12
13 #include "static_memory_gremlin.h"
14
15 #include <basis/functions.h>
16
17 #include <basis/array.h>
18 //temp!  needed for fake continuable error etc
19
20 #include <stdio.h>
21 #include <string.h>
22
23 using namespace basis;
24
25 namespace structures {
26
27 //#define DEBUG_STATIC_MEMORY_GREMLIN
28   // comment this out to eliminate debugging print-outs.  otherwise they
29   // are controlled by the class interface.
30
31 //#define SKIP_STATIC_CLEANUP
32   // don't uncomment this unless you want all static objects to be left
33   // allocated on shutdown of the program.  that's very sloppy but may
34   // sometimes be needed for testing.
35
36 //////////////
37
38 const int SMG_CHUNKING_FACTOR = 32;
39   // we'll allocate this many indices at a time.
40
41 //////////////
42
43 static bool __global_program_is_dying = false;
44   // this is set to true when no more logging or access to static objects
45   // should be allowed.
46
47 //////////////
48
49 class gremlin_object_record
50 {
51 public:
52   root_object *c_object;
53   const char *c_name;
54 };
55
56 //////////////
57
58 static_memory_gremlin::static_memory_gremlin()
59 : c_lock(),
60   c_top_index(0),
61   c_actual_size(0),
62   c_pointers(NULL_POINTER),
63   c_show_debugging(false)
64 {
65   ensure_space_exists();
66 }
67
68 static_memory_gremlin::~static_memory_gremlin()
69 {
70   __global_program_is_dying = true;
71     // now the rest of the program is on notice; we're practically gone.
72
73 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
74   if (c_show_debugging)
75     printf("SMG: beginning static object shutdown...\n");
76 #endif
77
78 #ifndef SKIP_STATIC_CLEANUP
79   // clean up any allocated pointers in reverse order of addition.
80   while (c_top_index > 0) {
81     // make sure we fixate on which guy is shutting down.  some new ones
82     // could be added on the end of the list as a result of this destruction.
83     int zapped_index = c_top_index - 1;
84     gremlin_object_record *ptr = c_pointers[zapped_index];
85     c_pointers[zapped_index] = NULL_POINTER;
86       // since we know the one we're zapping, we no longer need that index.
87     c_top_index--;
88       // this should allow us to keep chewing on items that are newly being
89       // added during static shutdown, since we have taken the current object
90       // entirely out of the picture.
91     if (ptr) {
92 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
93       if (c_show_debugging)
94         printf((astring("SMG: deleting ") + ptr->c_object->instance_name()
95             + " called " + ptr->c_name
96             + a_sprintf(" at index %d.\n", zapped_index) ).s());
97 #endif
98       WHACK(ptr->c_object);
99       WHACK(ptr);
100     }
101   }
102 #endif
103   delete [] c_pointers;
104   c_pointers = NULL_POINTER;
105 }
106
107 bool static_memory_gremlin::__program_is_dying() { return __global_program_is_dying; }
108
109 mutex &static_memory_gremlin::__memory_gremlin_synchronizer()
110 {
111   static mutex __globabl_synch_mem;
112   return __globabl_synch_mem;
113 }
114
115 int static_memory_gremlin::locate(const char *unique_name)
116 {
117   auto_synchronizer l(c_lock);
118   for (int i = 0; i < c_top_index; i++) {
119     if (!strcmp(c_pointers[i]->c_name, unique_name)) return i;
120   }
121   return common::NOT_FOUND;
122 }
123
124 root_object *static_memory_gremlin::get(const char *unique_name)
125 {
126   auto_synchronizer l(c_lock);
127   int indy = locate(unique_name);
128   if (negative(indy)) return NULL_POINTER;
129   return c_pointers[indy]->c_object;
130 }
131
132 const char *static_memory_gremlin::find(const root_object *ptr)
133 {
134   auto_synchronizer l(c_lock);
135   for (int i = 0; i < c_top_index; i++) {
136     if (ptr == c_pointers[i]->c_object)
137       return c_pointers[i]->c_name;
138   }
139   return NULL_POINTER;
140 }
141
142 bool static_memory_gremlin::put(const char *unique_name, root_object *to_put)
143 {
144   auto_synchronizer l(c_lock);
145   int indy = locate(unique_name);
146   // see if that name already exists.
147   if (non_negative(indy)) {
148 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
149     if (c_show_debugging)
150       printf((astring("SMG: cleaning out old object ")
151           + c_pointers[indy]->c_object->instance_name()
152           + " called " + c_pointers[indy]->c_name 
153           + " in favor of object " + to_put->instance_name()
154           + " called " + unique_name
155           + a_sprintf(" at index %d.\n", indy)).s());
156 #endif
157     WHACK(c_pointers[indy]->c_object);
158     c_pointers[indy]->c_object = to_put;
159     return true;
160   }
161 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
162   if (c_show_debugging)
163     printf((astring("SMG: storing ") + to_put->instance_name()
164         + " called " + unique_name
165         + a_sprintf(" at index %d.\n", c_top_index)).s());
166 #endif
167   ensure_space_exists();
168   c_pointers[c_top_index] = new gremlin_object_record;
169   c_pointers[c_top_index]->c_object = to_put;
170   c_pointers[c_top_index]->c_name = unique_name;
171   c_top_index++;
172   return true;
173 }
174
175 void static_memory_gremlin::ensure_space_exists()
176 {
177   auto_synchronizer l(c_lock);
178   if (!c_pointers || (c_top_index + 1 >= c_actual_size) ) {
179     // never had any contents yet or not enough space exists.
180 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
181     if (c_show_debugging)
182       printf(a_sprintf("SMG: adding space for top at %d.\n", c_top_index).s());
183 #endif
184     c_actual_size += SMG_CHUNKING_FACTOR;
185     typedef gremlin_object_record *base_ptr;
186     gremlin_object_record **new_ptr = new base_ptr[c_actual_size];
187     if (!new_ptr) {
188       throw "error: static_memory_gremlin::ensure_space_exists: failed to allocate memory for pointer list";
189     }
190     for (int i = 0; i < c_actual_size; i++) new_ptr[i] = NULL_POINTER;
191     for (int j = 0; j < c_actual_size - SMG_CHUNKING_FACTOR; j++) 
192       new_ptr[j] = c_pointers[j];
193     if (c_pointers) delete [] c_pointers;
194     c_pointers = new_ptr;
195   }
196 }
197
198 // this function ensures that the space for the global objects is kept until
199 // the program goes away.  if it's the first time through, then the gremlin
200 // gets created; otherwise the existing one is used.  this function should
201 // always be called by the main program before any attempts to use global
202 // features like SAFE_STATIC or the program wide logger.  it is crucial that no
203 // user-level threads have been created in the program before this is called.
204 static_memory_gremlin &static_memory_gremlin::__hoople_globals()
205 {
206   static bool _initted = false;  // tells whether we've gone through yet.
207   static static_memory_gremlin *_internal_gremlin = NULL_POINTER;
208     // holds our list of shared pieces...
209
210   if (!_initted) {
211 #ifdef DEBUG_STATIC_MEMORY_GREMLIN
212     printf("%s: initializing HOOPLE_GLOBALS now.\n", _global_argv[0]); 
213 #endif
214
215 #ifdef ENABLE_MEMORY_HOOK
216     void *temp = program_wide_memories().provide_memory(1, __FILE__, __LINE__);
217       // invoke now to get memory engine instantiated.
218     program_wide_memories().release_memory(temp);  // clean up junk.
219 #endif
220
221 #ifdef ENABLE_CALLSTACK_TRACKING
222     program_wide_stack_trace().full_trace_size();
223       // invoke now to get callback tracking instantiated.
224 #endif
225     FUNCDEF("HOOPLE_GLOBALS remainder");
226       // this definition must be postponed until after the objects that would
227       // track it actually exist.
228     if (func) {}  // shut up the compiler about using it.
229
230     // this simple approach is not going to succeed if the SAFE_STATIC macros
231     // are used in a static library which is then used in more than one dynamic
232     // library on win32.  this is because each dll in win32 will have a
233     // different version of certain static objects that should only occur once
234     // per program.  this problem is due to the win32 memory model, but in
235     // hoople at least this has been prevented; our only static library that
236     // appears in a bunch of dlls is basis and it is not allowed to use the
237     // SAFE_STATIC macro.
238     _internal_gremlin = new static_memory_gremlin;
239     _initted = true;
240   }
241
242   return *_internal_gremlin;
243 }
244
245 //////////////
246
247 } // namespace.
248