Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
mem.h
Go to the documentation of this file.
1 /*************************************************************************
2  * PLEASE SEE THE FILE "license.txt" (INCLUDED WITH THIS SOFTWARE PACKAGE)
3  * FOR LICENSE AND COPYRIGHT INFORMATION.
4  *************************************************************************/
5 
6 /* ======================================================================
7  mem.h
8 
9  Init_memory_utilities() should be called before any of these routines
10  are used.
11 
12  Basic memory allocation utilities:
13 
14  All memory blocks are allocated via calls to allocate_memory(). It
15  calls malloc() and aborts if we run out of memory. Free_memory() is
16  the inverse of allocate_memory(). Allocate_memory_and_zerofill()
17  does the obvious thing. These routines take a usage_code indicating
18  what purpose the memory is for (hash tables, strings, etc.). This
19  is used purely for statistics keeping.
20 
21  Print_memory_statistics() prints out stats on the memory usage.
22 
23  String utilities:
24 
25  Make_memory_block_for_string() takes a pointer to a string, allocates
26  a memory block just large enough to hold the string, and copies the
27  string into the block. Free_memory_block_for_string() frees the
28  block.
29 
30  "Growable strings" provide a convenient way of building up a string
31  piece by piece without having to pre-allocate the right amount of
32  memory. To initialize one, say "gs = make_blank_growable_string()"
33  where "gs" is of type "growable_string". To concatenate a new string
34  onto the end of an existing growable string, call
35  add_to_growable_string(&gs,new_string) [note the "&"]. When you're
36  done using it, call free_growable_string(gs). Growable strings are
37  implemented by allocating a block of memory large enough to hold
38  (1) the memory block's size, (2) the current string length, and (3)
39  the current text of the string. Add_to_growable_string() may result
40  in a new (larger) memory block being allocated and the text of the
41  string being copied. Three macros provide access to a growable string's
42  parts: memsize_of_growable_string(), length_of_growable_string(),
43  and (most importantly) text_of_growable_string(), which is of type
44  (char *).
45 
46  Memory pools:
47 
48  To allocate and free memory items efficiently at run time, we use
49  pools of small fixed-size items and do allocation and freeing using
50  inline macros. Different memory pools are used for different things
51  and contain different size items. Each pool consists of a memory_pool
52  structure (used for maintaining the pool) and a chain of big blocks
53  of memory (currently about 32K per block) obtained from allocate_memory().
54  We maintain a free_list of small items not being used, and allocate by
55  grabbing the first item on the free list. If the free list is empty,
56  we add another big block to the pool.
57 
58  Init_memory_pool() should be called to initialize a memory_pool
59  structure before it is used. After that, the macro forms
60  allocate_with_pool (&mem_pool, &pointer_to_be_set_to_new_item) and
61  free_with_pool (&mem_pool, pointer_to_item)
62  are used to allocate and free items. Print_memory_pool_statistics()
63  prints stats about the various pools in use and how much memory each
64  is using.
65 
66  Cons cell and list utilities:
67 
68  This provides a simple facility for manipulating generic lists, just
69  like in Lisp. A "cons" is a pair of pointers; a "list" is just a cons
70  (or NIL). We maintain a memory pool (see above) of conses.
71  Allocate_cons() is like allocate_with_pool() for conses; free_cons()
72  is like free_with_pool. Push(new_item,my_list) is a macro for adding
73  a new item onto the front of an existing list.
74 
75  In addition to the regular conses, we also have a pool of "dl_cons"es--
76  these are like conses, only doubly-linked. A "dl_list" is a just a
77  dl_cons (or NIL).
78 
79  Some (consed) list utility functions: destructively_reverse_list()
80  does just what it says, and returns a pointer to the new head of the
81  list (formerly the tail). Member_of_list() tests membership, using
82  "==" as the equality predicates. Add_if_not_member() is like Lisp's
83  "pushnew"; it returns the new list (possibly unchanged) list.
84  Free_list() frees all the cons cells in the list.
85 
86  Sometimes we need to surgically extract particular elements from a
87  list. Extract_list_elements() and extract_dl_list_elements() do this.
88  They use a callback function that indicates which elements to extract:
89  the callback function is called on each element of the list, and should
90  return TRUE for the elements to be extracted. The two extraction
91  functions return a list (or dl_list) of the extracted elements.
92 
93  Hash table routines:
94 
95  We use hash tables in various places, and don't want to have to fix
96  their size ahead of time. These routines provide hash tables that
97  are dynamically resized as items are added and removed. We use
98  "open hashing" with a hash table whose size is a power of two. We
99  keep track of how many items are in the table. The table grows
100  when # of items >= 2*size, and shrinks when # of items < size/2.
101  To resize a hash table, we rehash every item in it.
102 
103  Each item must be a structure whose first field is reserved for use
104  by these hash table routines--it points to the next item in the hash
105  bucket. Hash tables are created and initialized via make_hash_table();
106  you give it a hash function (i.e., a C function) that finds the hash
107  value for a given item, hashing it to a value num_bits wide. For aid
108  in this, we provide masks_for_n_low_order_bits[] that select out the
109  low-order bits of a value: (x & masks_for_n_low_order_bits[23]) picks
110  out the 23 low-order bits of x.
111 
112  Items are added/removed from a hash table via add_to_hash_table() and
113  remove_from_hash_table(). These calls resize the hash table if
114  necessary.
115 
116  The contents of a hash table (or one bucket in the table) can be
117  retrieved via do_for_all_items_in_hash_table() and
118  do_for_all_items_in_hash_bucket(). Each uses a callback function,
119  invoking it with each successive item. The callback function should
120  normally return FALSE. If the callback function ever returns TRUE,
121  iteration over the hash table items stops and the do_for_xxx()
122  routine returns immediately.
123 ====================================================================== */
124 
125 #ifndef MEM_H
126 #define MEM_H
127 
128 #include "chunk.h"
129 #include "kernel.h"
130 
131 #include <stdio.h> // Needed for FILE token below
132 #include <string.h> // Needed for strlen, etc. below
133 
134 #ifndef _WIN32
135 #include <strings.h>
136 #include <stdlib.h> // malloc
137 #endif // !_WIN32
138 
139 #ifdef __cplusplus
140 //extern "C"
141 //{
142 #endif
143 
144 extern void init_memory_utilities (agent* thisAgent);
145 
146 /* ----------------------- */
147 /* basic memory allocation */
148 /* ----------------------- */
149 
150 #ifdef DEBUG_MEMORY
151 
152 #ifdef USE_MACROS
153 #define fill_with_garbage(block,size) memset((void *)(block), 0xBB, (size))
154 #else // !USE_MACROS
155 #ifdef __cplusplus
156 //}
157 #endif // __cplusplus
158 
159 template <typename T>
160 inline void fill_with_garbage(T * block, size_t size)
161 {
162  memset(static_cast<void *>(block), 0xBB, (size));
163 }
164 
165 #ifdef __cplusplus
166 //extern "C"
167 //{
168 #endif // __cplusplus
169 
170 #endif // !USE_MACROS
171 
172 #else
173 
174 #define fill_with_garbage(block,size) { }
175 
176 #endif
177 
178 #define MISCELLANEOUS_MEM_USAGE 0
179 #define HASH_TABLE_MEM_USAGE 1
180 #define STRING_MEM_USAGE 2
181 #define POOL_MEM_USAGE 3
182 #define STATS_OVERHEAD_MEM_USAGE 4
183 
184 #define NUM_MEM_USAGE_CODES 5
185 
186 extern void *allocate_memory (agent* thisAgent, size_t size, int usage_code);
187 extern void *allocate_memory_and_zerofill (agent* thisAgent, size_t size, int usage_code);
188 extern void free_memory (agent* thisAgent, void *mem, int usage_code);
189 extern void print_memory_statistics (agent* thisAgent);
190 
191 /* ---------------- */
192 /* string utilities */
193 /* ---------------- */
194 
195 extern char *make_memory_block_for_string (agent* thisAgent, char const*s);
196 extern void free_memory_block_for_string (agent* thisAgent, char *p);
197 
198 typedef char Bool;
199 typedef void * growable_string;
200 
201 #ifdef USE_MACROS
202 
203 #define savestring(x) (char *) strcpy ((char *)(malloc (strlen (x) + 1)), (x))
204 #define memsize_of_growable_string(gs) (*((int *)(gs)))
205 #define length_of_growable_string(gs) (*(((int *)(gs))+1))
206 #define text_of_growable_string(gs) (((char *)(gs)) + 2*sizeof(int *))
207 
208 #else
209 
210 // voigtjr 11/2005: platform specific code (strlen/malloc/etc) should be in .cpp files!
211 // except it can't be (?) because of the inline restriction
212 inline char * savestring(char * x)
213 {
214  return strcpy(static_cast<char *>(malloc (strlen (x) + 1)), (x));
215 }
216 
217 inline int & memsize_of_growable_string(growable_string gs)
218 {
219  return (*((int *)(gs)));
220 }
221 
222 inline int & length_of_growable_string(growable_string gs)
223 {
224  return (*(((int *)(gs))+1));
225 }
226 
227 inline char * text_of_growable_string(growable_string gs)
228 {
229  return (((char *)(gs)) + 2*sizeof(int *));
230 }
231 
232 #endif /* USE_MACROS */
233 
234 extern growable_string make_blank_growable_string (agent* thisAgent);
235 extern void add_to_growable_string (agent* thisAgent, growable_string *gs, const char *string_to_add);
236 extern void free_growable_string (agent* thisAgent, growable_string gs);
237 
238 /* ------------ */
239 /* memory pools */
240 /* ------------ */
241 
242 #define MAX_POOL_NAME_LENGTH 15
243 
244 typedef struct memory_pool_struct {
245  void *free_list; /* header of chain of free items */
246  size_t used_count; /* used for statistics only when #def'd MEMORY_POOL_STATS */
247  size_t item_size; /* bytes per item */
248  size_t items_per_block; /* number of items in each big block */
249  size_t num_blocks; /* number of big blocks in use by this pool */
250  void *first_block; /* header of chain of blocks */
251  char name[MAX_POOL_NAME_LENGTH]; /* name of the pool (for memory-stats) */
252  struct memory_pool_struct *next; /* next in list of all memory pools */
253 } memory_pool;
254 
255 extern void add_block_to_memory_pool (agent* thisAgent, memory_pool *p);
256 extern void init_memory_pool (agent* thisAgent, memory_pool *p, size_t item_size, const char *name);
257 extern void free_memory_pool (agent*, memory_pool *p); /* RPM 6/09, with help from AMN */
258 
259 #ifdef MEMORY_POOL_STATS
260 
261 #ifdef USE_MACROS
262 
263 #define increment_used_count(p) {(p)->used_count++;}
264 #define decrement_used_count(p) {(p)->used_count--;}
265 
266 #else
267 
268 #ifdef __cplusplus
269 //}
270 #endif
271 
272 template <typename P>
273 inline void increment_used_count(P p)
274 {
275  (p)->used_count++;
276 }
277 
278 template <typename P>
279 inline void decrement_used_count(P p)
280 {
281  (p)->used_count--;
282 }
283 
284 #ifdef __cplusplus
285 //extern "C"
286 //{
287 #endif
288 
289 #endif /* USE_MACROS */
290 
291 #else
292 
293 #define increment_used_count(p) { }
294 #define decrement_used_count(p) { }
295 
296 #endif /* MEMORY_POOL_STATS */
297 
298 #ifdef __cplusplus
299 //}
300 
301 #define MEM_POOLS_ENABLED 1
302 
303 template <typename T>
304 inline void allocate_with_pool(agent* thisAgent, memory_pool* p, T** dest_item_pointer)
305 {
306 
307 #if MEM_POOLS_ENABLED
308  // if there's no memory blocks left in the pool, then allocate a new one
309  if (! (p)->free_list) add_block_to_memory_pool(thisAgent, p);
310  // take the beginning of the next free block and give it to the T pointer
311  *(dest_item_pointer) = static_cast< T* > ((p)->free_list);
312  // we think this line increments free_list to the next available memory block
313  // we thought it took advantage of the fact that free_list is the first
314  // member of memory_pool, but I tried changing that and it still works, so now I'm at a loss
315  // if it helps, we think this line is equivalent to the following
316  // (at least, everything appears to work properly if you swap these lines):
317  // (p)->free_list = (*static_cast<P*>(dest_item_pointer))->free_list;
318  (p)->free_list = *(void * *)(*(dest_item_pointer));
319 
320  fill_with_garbage (*(dest_item_pointer), (p)->item_size);
322 
323 #else // !MEM_POOLS_ENABLED
324  // this is for debugging -- it disables the memory pool usage and just allocates
325  // new memory every time. If you want to use it, be sure to make the corresponding
326  // change to free_with_pool below
327  *dest_item_pointer = static_cast< T * > (malloc(sizeof(T)));
328 
329  // simply prevents compiler warnings when memory pools disabled
330  thisAgent=thisAgent;
331  p=p;
332 
333 #endif // !MEM_POOLS_ENABLED
334 }
335 
336 template <typename T>
337 inline void free_with_pool(memory_pool* p, T * item)
338 {
339 #if MEM_POOLS_ENABLED
340  fill_with_garbage ((item), (p)->item_size);
341  *(void * *)(item) = (p)->free_list;
342  (p)->free_list = (void *)(item);
344 
345 #else // !MEM_POOLS_ENABLED
346  // this is for debugging -- it disables the memory pool usage and just deallocates
347  // the memory every time. If you want to use it, be sure to make the corresponding
348  // change to allocate_with_pool above
349  free(item);
350 
351  // simply prevents compiler warnings when memory pools disabled
352  p=p;
353 #endif // !MEM_POOLS_ENABLED
354 }
355 
356 //extern "C"
357 //{
358 
359 #endif
360 
361 /* ---------------------------------------------------------------------
362  Macros for Inserting and Removing Stuff from Doubly-Linked Lists
363 
364  Note: fast_remove_from_dll() is the same as remove_from_dll() except
365  slightly faster. I (RBD) only realized this after writing all the
366  original code. With fast_remove_from_dll(), you have to tell it
367  the type (i.e., structure name) of the item being spliced out of
368  the list. At some point we might want to go through all the code
369  and have it use fast_remove_from_dll(), but it's probably not worth
370  the effort right now.
371 -------------------------------------------------------------------- */
372 
373 /* This macro cannot be easily converted to an inline function.
374  Some additional changes are required.
375 */
376 #define insert_at_head_of_dll(header,item,next_field_name,prev_field_name) { \
377  ((item)->next_field_name) = (header) ; \
378  ((item)->prev_field_name) = NIL ; \
379  if (header) ((header)->prev_field_name) = (item) ; \
380  (header) = (item) ; }
381 /*template <typename T>
382 inline void insert_at_head_of_dll(T header, T item, T next_field_name,
383  T prev_field_name)
384 {
385  ((item)->next_field_name) = (header);
386  ((item)->prev_field_name) = NIL;
387  if (header) ((header)->prev_field_name) = (item);
388  (header) = (item);
389 }*/
390 
391 /* This macro cannot be easily converted to an inline function.
392  Some additional changes are required.
393 */
394 #define remove_from_dll(header,item,next_field_name,prev_field_name) { \
395  if ((item)->next_field_name) \
396  ((item)->next_field_name->prev_field_name) = ((item)->prev_field_name); \
397  if ((item)->prev_field_name) { \
398  ((item)->prev_field_name->next_field_name) = ((item)->next_field_name); \
399  } else { \
400  (header) = ((item)->next_field_name); \
401  } }
402 /*template <typename T>
403 inline void remove_from_dll(T header, T item, T next_field_name,
404  T prev_field_name)
405 {
406  if ((item)->next_field_name)
407  ((item)->next_field_name->prev_field_name) = ((item)->prev_field_name);
408  if ((item)->prev_field_name) {
409  ((item)->prev_field_name->next_field_name) = ((item)->next_field_name);
410  } else {
411  (header) = ((item)->next_field_name);
412  }
413 }*/
414 
415 /* This macro cannot be easily converted to an inline function.
416  Some additional changes are required.
417 */
418 #define fast_remove_from_dll(header,item,typename,next_field_name,prev_field_name) { \
419  typename *tempnext, *tempprev; \
420  tempnext = (item)->next_field_name; \
421  tempprev = (item)->prev_field_name; \
422  if (tempnext) tempnext->prev_field_name = tempprev; \
423  if (tempprev) { \
424  tempprev->next_field_name = tempnext; \
425  } else { \
426  (header) = tempnext; } }
427 
428 /* ------------------------- */
429 /* Cons cell, list utilities */
430 /* ------------------------- */
431 
432 typedef struct cons_struct {
433  void *first;
434  struct cons_struct *rest;
435 } cons;
436 
437 typedef cons list;
438 
439 typedef struct dl_cons_struct {
440  void *item;
443 } dl_cons;
444 
445 typedef dl_cons dl_list;
446 
448 extern Bool member_of_list (void *item, ::list *the_list);
449 extern ::list *add_if_not_member (agent* thisAgent, void *item, ::list *old_list);
450 extern void free_list (agent* thisAgent, ::list *the_list);
451 
452 /* Added a void* parameter to cons_test_fn, because remove_pwatch_test_fn(),
453  one of the callback functions, requires a third parameter that points to a
454  production. In the future, other callback functions of type cons_test_fn may
455  need parameters of different types, so a void pointer is best. -AJC (8/7/02) */
456 /* Added thisAgent to cons_test_fn type, because we are eliminating the
457  global soar_agent. -AJC (8/7/02) */
458 //typedef Bool (*cons_test_fn)(cons *c);
459 typedef Bool (*cons_test_fn)(agent* thisAgent, cons *c, void* data);
460 
461 typedef Bool (*dl_cons_test_fn)(dl_cons *dc, agent* thisAgent);
462 
463 /* Added a void* parameter to extract_list_elements, because remove_pwatch_test_fn(),
464  one of the callback functions, requires a third parameter that points to a
465  production. In the future, other callback functions of type cons_test_fn may
466  need parameters of different types, so a void pointer is best. -AJC (8/7/02) */
467 extern ::list *extract_list_elements (agent* thisAgent, ::list **header, cons_test_fn f, void* data = 0);
468 
469 extern dl_list *extract_dl_list_elements (agent* thisAgent, dl_list **header, dl_cons_test_fn f);
470 
471 extern Bool cons_equality_fn (agent*, cons *c, void *data);
472 
473 /* ----------------------------- */
474 /* Resizable hash table routines */
475 /* ----------------------------- */
476 
478 
479 typedef uint32_t ((*hash_function)(void *item, short num_bits));
480 
483  char data;
485 
487 
488 typedef struct hash_table_struct {
489  int64_t count; /* number of items in the table */
490  uint32_t size; /* number of buckets */
491  short log2size; /* log (base 2) of size */
492  short minimum_log2size; /* table never shrinks below this size */
493  bucket_array *buckets;
494  hash_function h; /* call this to hash or rehash an item */
495 } hash_table;
496 
497 extern struct hash_table_struct *make_hash_table (agent* thisAgent, short minimum_log2size,
498  hash_function h);
499 extern void free_hash_table(agent* thisAgent, struct hash_table_struct *ht); /* RPM 6/09 */
500 extern void remove_from_hash_table (agent* thisAgent, struct hash_table_struct *ht, void *item);
501 extern void add_to_hash_table (agent* thisAgent, struct hash_table_struct *ht, void *item);
502 
503 typedef Bool (*hash_table_callback_fn)(void *item);
504 typedef Bool (*hash_table_callback_fn2)(agent* thisAgent, void *item, void* f);
505 
506 extern void do_for_all_items_in_hash_table (agent* thisAgent, struct hash_table_struct *ht,
507  hash_table_callback_fn2 f, void* userdata);
508 extern void do_for_all_items_in_hash_bucket (struct hash_table_struct *ht,
510  uint32_t hash_value);
511 
512 #ifdef __cplusplus
513 //}
514 #endif
515 
516 #endif
517 
518