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