Soar Kernel  9.3.2 08-06-12
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
mem.cpp
Go to the documentation of this file.
1 #include <portability.h>
2 
3 /*************************************************************************
4  * PLEASE SEE THE FILE "license.txt" (INCLUDED WITH THIS SOFTWARE PACKAGE)
5  * FOR LICENSE AND COPYRIGHT INFORMATION.
6  *************************************************************************/
7 
8 /*************************************************************************
9  *
10  * file: mem.cpp
11  *
12  * ====================================================================
13  * Memory allocation and deallocation, string, linked list, and resizable
14  * hash table utilities for Soar 6. see soarkernel.h for more comments
15  * Init_memory_utilities() should be called before any of these routines
16  * are used.
17  * =======================================================================
18  */
19 
20 #include <stdlib.h>
21 
22 #include "mem.h"
23 #include "kernel.h"
24 #include "agent.h"
25 #include "init_soar.h"
26 #include "print.h"
27 
28 #include <assert.h>
29 
30 /* ====================================================================
31 
32  Basic Memory Allocation Utilities
33 
34  All memory blocks are allocated via calls to allocate_memory(). It
35  calls malloc() and aborts if we run out of memory. Free_memory() is
36  the inverse of allocate_memory(). Allocate_memory_and_zerofill()
37  does the obvious thing. These routines take a usage_code indicating
38  what purpose the memory is for (hash tables, strings, etc.). This
39  is used purely for statistics keeping.
40 
41  Print_memory_statistics() prints out stats on the memory usage.
42 ==================================================================== */
43 
44 void *allocate_memory (agent* thisAgent, size_t size, int usage_code) {
45  char *p;
46 
47  thisAgent->memory_for_usage[usage_code] += size;
48  size += sizeof(size_t);
49  thisAgent->memory_for_usage[STATS_OVERHEAD_MEM_USAGE] += sizeof(size_t);
50 
51  p = static_cast<char *>(malloc (size));
52  if (p==NULL) {
53  char msg[BUFFER_MSG_SIZE];
54  SNPRINTF(msg, BUFFER_MSG_SIZE, "\nmem.c: Error: Tried but failed to allocate %llu bytes of memory.\n", static_cast<long long unsigned>(size));
55  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
56  abort_with_fatal_error (thisAgent, msg);
57  }
58  if (reinterpret_cast<uintptr_t>(p) & 3) {
59  char msg[BUFFER_MSG_SIZE];
60  strncpy (msg,"\nmem.c: Error: Memory allocator returned an address that's not a multiple of 4.\n", BUFFER_MSG_SIZE);
61  msg[BUFFER_MSG_SIZE - 1] = 0; /* ensure null termination */
62  abort_with_fatal_error(thisAgent, msg);
63  }
64 
65  fill_with_garbage (p, size);
66 
67  *(reinterpret_cast<size_t *>(p)) = size;
68  p += sizeof(size_t);
69 
70  return p;
71 }
72 
73 void *allocate_memory_and_zerofill (agent* thisAgent, size_t size, int usage_code) {
74  void *p;
75 
76  p = allocate_memory (thisAgent, size, usage_code);
77  memset (p, 0, size);
78  return p;
79 }
80 
81 void free_memory (agent* thisAgent, void *mem, int usage_code) {
82  size_t size;
83 
84  if ( mem == 0 ) return;
85 
86  mem = static_cast<char *>(mem) - sizeof(size_t);
87  size = *(static_cast<size_t *>(mem));
88  fill_with_garbage (mem, size);
89 
90  thisAgent->memory_for_usage[STATS_OVERHEAD_MEM_USAGE] -= sizeof(size_t);
91  thisAgent->memory_for_usage[usage_code] -= (size - sizeof(size_t));
92 
93  free (mem);
94 }
95 
96 void print_memory_statistics (agent* thisAgent) {
97  size_t total;
98  int i;
99 
100  total = 0;
101  for (i=0; i<NUM_MEM_USAGE_CODES; i++) total += thisAgent->memory_for_usage[i];
102 
103  print (thisAgent, "%8lu bytes total memory allocated\n", total);
104  print(thisAgent, "%8lu bytes statistics overhead\n",
106  print(thisAgent, "%8lu bytes for strings\n",
107  thisAgent->memory_for_usage[STRING_MEM_USAGE]);
108  print(thisAgent, "%8lu bytes for hash tables\n",
110  print(thisAgent, "%8lu bytes for various memory pools\n",
111  thisAgent->memory_for_usage[POOL_MEM_USAGE]);
112  print(thisAgent, "%8lu bytes for miscellaneous other things\n",
114 }
115 
116 /* ====================================================================
117 
118  String Utilities
119 
120  Make_memory_block_for_string() takes a pointer to a string, allocates
121  a memory block just large enough to hold the string, and copies the
122  string into the block. Free_memory_block_for_string() frees the
123  block.
124 
125  "Growable strings" provide a convenient way of building up a string
126  piece by piece without having to pre-allocate the right amount of
127  memory. To initialize one, say "gs = make_blank_growable_string()"
128  where "gs" is of type "growable_string". To concatenate a new string
129  onto the end of an existing growable string, call
130  add_to_growable_string(&gs,new_string) [note the "&"]. When you're
131  done using it, call free_growable_string(gs). Growable strings are
132  implemented by allocating a block of memory large enough to hold
133  (1) the memory block's size, (2) the current string length, and (3)
134  the current text of the string. Add_to_growable_string() may result
135  in a new (larger) memory block being allocated and the text of the
136  string being copied. Three macros provide access to a growable string's
137  parts: memsize_of_growable_string(), length_of_growable_string(),
138  and (most importantly) text_of_growable_string(), which is of type
139  (char *).
140 ==================================================================== */
141 
142 char *make_memory_block_for_string (agent* thisAgent, char const*s) {
143  char *p;
144  size_t size;
145 
146  size = strlen(s)+1; /* plus one for trailing null character */
147  p = static_cast<char *>(allocate_memory (thisAgent, size, STRING_MEM_USAGE));
148  strncpy(p,s,size);
149  p[size-1] = 0; /* ensure null termination */
150  return p;
151 }
152 
153 void free_memory_block_for_string (agent* thisAgent, char *p) {
154  free_memory (thisAgent, p, STRING_MEM_USAGE);
155 }
156 
157 #define INITIAL_GROWABLE_STRING_SIZE 100
158 
160  growable_string gs;
161 
162  gs = allocate_memory (thisAgent, 2*sizeof(int *) + INITIAL_GROWABLE_STRING_SIZE,
166  *(text_of_growable_string(gs)) = 0;
167  return gs;
168 }
169 
171  const char *string_to_add) {
172  size_t current_length, length_to_add, new_length, new_memsize;
173  growable_string New;
174 
175  current_length = length_of_growable_string(*gs);
176  length_to_add = strlen (string_to_add);
177  new_length = current_length + length_to_add;
178  if (new_length + 1 > static_cast<size_t>(memsize_of_growable_string(*gs))) {
179  new_memsize = memsize_of_growable_string(*gs);
180  while (new_length + 1 > new_memsize) new_memsize = new_memsize * 2;
181  New = allocate_memory (thisAgent, new_memsize + 2*sizeof(int *), STRING_MEM_USAGE);
182  memsize_of_growable_string(New) = static_cast<int>(new_memsize);
184  free_memory (thisAgent, *gs, STRING_MEM_USAGE);
185  *gs = New;
186  }
187  strcpy (text_of_growable_string(*gs)+current_length, string_to_add);
188  length_of_growable_string(*gs) = static_cast<int>(new_length);
189 }
190 
192  free_memory (thisAgent, gs, STRING_MEM_USAGE);
193 }
194 
195 /* ====================================================================
196 
197  Memory Pool Routines
198 
199  To allocate and free memory items efficiently at run time, we use
200  pools of small fixed-size items and do allocation and freeing using
201  inline macros. Different memory pools are used for different things
202  and contain different size items. Each pool consists of a memory_pool
203  structure (used for maintaining the pool) and a chain of big blocks
204  of memory (currently about 32K per block) obtained from allocate_memory().
205  We maintain a free_list of small items not being used, and allocate by
206  grabbing the first item on the free list. If the free list is empty,
207  we add another big block to the pool.
208 
209  Init_memory_pool() should be called to initialize a memory_pool
210  structure before it is used. After that, the macro forms [see soarkernel.h]
211  allocate_with_pool (&mem_pool, &pointer_to_be_set_to_new_item) and
212  free_with_pool (&mem_pool, pointer_to_item)
213  are used to allocate and free items. Print_memory_pool_statistics()
214  prints stats about the various pools in use and how much memory each
215  is using.
216 ==================================================================== */
217 
218 #define DEFAULT_INTERLEAVE_FACTOR 1
219 /* should be 1 for maximum speed, but to avoid a gradual slowdown due
220  to a gradually decreasing CPU cache hit ratio, make this a larger
221  number, must be prime */
222 #define DEFAULT_BLOCK_SIZE 0x7FF0 /* about 32K bytes per block */
223 
225  char *new_block;
226  size_t size, i, item_num, interleave_factor;
227  char *item, *prev_item;
228 
229  /* --- allocate a new block for the pool --- */
230  size = p->item_size * p->items_per_block + sizeof(char *);
231  new_block = static_cast<char *>(allocate_memory (thisAgent, size, POOL_MEM_USAGE));
232  *(char * *)new_block = static_cast<char *>(p->first_block);
233  p->first_block = new_block;
234  p->num_blocks++;
235 
236  /* somewhere in here, need to check if total mem usage exceeds limit set by user
237  we only check when increasing pools, because the other memories are small by comparison,
238  we shouldn't check for every block added to any pool, since that is unduly expensive
239  can we keep a block counter on the agent and check it modulo some function of the limit?
240  */
241  /*
242  uint64_t total = 0;
243  for (i=0; i<NUM_MEM_USAGE_CODES; i++) total += thisAgent->memory_for_usage[i];
244 
245  if (total > thisAgent->sysparams[MAX_MEMORY_USAGE_SYSPARAM]) {
246  soar_invoke_callbacks(thisAgent, thisAgent,
247  MAX_MEMORY_USAGE_CALLBACK,
248  (soar_call_data) NULL);
249  print (thisAgent, "%8lu bytes total memory allocated\n", total);
250  print (thisAgent, "exceeds total allowed for Soar: %8lu bytes \n",
251  thisAgent->sysparams[MAX_MEMORY_USAGE_SYSPARAM]);
252  }
253 
254  */
255 
256  /* --- link up the new entries onto the free list --- */
257  interleave_factor = DEFAULT_INTERLEAVE_FACTOR;
258  if (interleave_factor >= p->items_per_block) interleave_factor = 1;
259 
260  item_num = interleave_factor;
261  prev_item = new_block + sizeof(char *); /* prev_item is item number 0 */
262  for (i=0; i < p->items_per_block - 1; i++) {
263  item = new_block + sizeof(char *) + item_num * p->item_size;
264  *(char * *)prev_item = item;
265  prev_item = item;
266  item_num = item_num + interleave_factor;
267  if (item_num >= p->items_per_block) item_num -= p->items_per_block;
268  }
269  *(char * *)prev_item = static_cast<char *>(p->free_list);
270  p->free_list = new_block + sizeof(char *);
271 }
272 
273 /* RPM 6/09, with help from AMN */
274 void free_memory_pool (agent* thisAgent, memory_pool *p) {
275 
276  char *cur_block = static_cast<char*>(p->first_block);
277  char *next_block;
278  for(size_t i=0; i<p->num_blocks; i++) {
279  // the first 4 bytes point to the next block
280  next_block = *(char **)cur_block;
281  free_memory(thisAgent, cur_block, POOL_MEM_USAGE);
282  cur_block = next_block;
283  }
284  p->num_blocks = 0;
285 }
286 
287 void init_memory_pool (agent* thisAgent, memory_pool *p, size_t item_size, const char *name) {
288  if (item_size < sizeof(char *)) item_size = sizeof(char *);
289  while (item_size & 3) item_size++; /* make sure item_size is multiple of 4 */
290  p->item_size = item_size;
291  p->items_per_block = DEFAULT_BLOCK_SIZE / item_size;
292  p->num_blocks = 0;
293  p->first_block = NIL;
294  p->free_list = NIL;
295 #ifdef MEMORY_POOL_STATS
296  p->used_count = 0;
297 #endif
298  p->next = thisAgent->memory_pools_in_use;
299  thisAgent->memory_pools_in_use = p;
300  if (strlen(name) > MAX_POOL_NAME_LENGTH) {
301  char msg[2*MAX_POOL_NAME_LENGTH];
302  SNPRINTF(msg, 2*MAX_POOL_NAME_LENGTH, "mem.c: Internal error: memory pool name too long: %s\n",name);
303  msg[2*MAX_POOL_NAME_LENGTH - 1] = 0; /* ensure null termination */
304  abort_with_fatal_error(thisAgent, msg);
305  }
306  strncpy(p->name,name,MAX_POOL_NAME_LENGTH);
307  p->name[MAX_POOL_NAME_LENGTH - 1] = 0; /* ensure null termination */
308 }
309 
310 /* ====================================================================
311 
312  Cons Cell and List Utilities
313 
314  This provides a simple facility for manipulating generic lists, just
315  like in Lisp. A "cons" is a pair of pointers; a "list" is just a cons
316  (or NIL). We maintain a memory pool (see above) of conses.
317  Allocate_cons() is like allocate_with_pool() for conses; free_cons()
318  is like free_with_pool. Push(new_item,my_list) is a macro for adding
319  a new item onto the front of an existing list.
320 
321  In addition to the regular conses, we also have a pool of "dl_cons"es--
322  these are like conses, only doubly-linked. A "dl_list" is a just a
323  dl_cons (or NIL).
324 
325  Some (consed) list utility functions: destructively_reverse_list()
326  does just what it says, and returns a pointer to the new head of the
327  list (formerly the tail). Member_of_list() tests membership, using
328  "==" as the equality predicates. Add_if_not_member() is like Lisp's
329  "pushnew"; it returns the new list (possibly unchanged) list.
330  Free_list() frees all the cons cells in the list.
331 
332  Sometimes we need to surgically extract particular elements from a
333  list. Extract_list_elements() and extract_dl_list_elements() do this.
334  They use a callback function that indicates which elements to extract:
335  the callback function is called on each element of the list, and should
336  return TRUE for the elements to be extracted. The two extraction
337  functions return a list (or dl_list) of the extracted elements.
338 ==================================================================== */
339 
341  cons *prev, *current, *next;
342 
343  prev = NIL;
344  current = c;
345  while (current) {
346  next = current->rest;
347  current->rest = prev;
348  prev = current;
349  current = next;
350  }
351  return prev;
352 }
353 
354 Bool member_of_list (void *item, list *the_list) {
355  while (the_list) {
356  if (the_list->first == item) return TRUE;
357  the_list = the_list->rest;
358  }
359  return FALSE;
360 }
361 
362 list *add_if_not_member (agent* thisAgent, void *item, list *old_list) {
363  cons *c;
364 
365  for (c=old_list; c!=NIL; c=c->rest)
366  if (c->first==item) return old_list;
367  allocate_cons (thisAgent, &c);
368  c->first = item;
369  c->rest = old_list;
370  return c;
371 }
372 
373 void free_list (agent* thisAgent, list *the_list) {
374  cons *c;
375 
376  while (the_list) {
377  c = the_list;
378  the_list = the_list->rest;
379  free_cons (thisAgent, c);
380  }
381 }
382 
383 list *extract_list_elements (agent* thisAgent, list **header, cons_test_fn f, void* data)
384 {
385  cons *first_extracted_element, *tail_of_extracted_elements;
386  cons *c, *prev_c, *next_c;
387 
388  first_extracted_element = NIL;
389  tail_of_extracted_elements = NIL;
390 
391  prev_c = NIL;
392  for (c=(*header); c!=NIL; c=next_c)
393  {
394  next_c = c->rest;
395  if (!f(thisAgent, c, data))
396  {
397  prev_c = c;
398  continue;
399  }
400 
401  if (prev_c)
402  prev_c->rest = next_c;
403  else
404  *header = next_c;
405  if (first_extracted_element)
406  tail_of_extracted_elements->rest = c;
407  else
408  first_extracted_element = c;
409 
410  tail_of_extracted_elements = c;
411  }
412 
413  if (first_extracted_element)
414  tail_of_extracted_elements->rest = NIL;
415 
416  return first_extracted_element;
417 }
418 
420 {
421  dl_cons *first_extracted_element, *tail_of_extracted_elements;
422  dl_cons *dc, *next_dc;
423 
424  first_extracted_element = NIL;
425  tail_of_extracted_elements = NIL;
426 
427  for (dc=(*header); dc!=NIL; dc=next_dc)
428  {
429  next_dc = dc->next;
430 
431  if (!f(dc,thisAgent))
432  continue;
433 
434  remove_from_dll ((*header), dc, next, prev);
435 
436  if (first_extracted_element)
437  tail_of_extracted_elements->next = dc;
438  else
439  first_extracted_element = dc;
440 
441  dc->prev = tail_of_extracted_elements;
442  tail_of_extracted_elements = dc;
443  }
444 
445 /************************************************************************/
446 
447  if (first_extracted_element)
448  tail_of_extracted_elements->next = NIL;
449 
450  return first_extracted_element;
451 }
452 
453 Bool cons_equality_fn (agent*, cons *c, void *data)
454 {
455  return (c->first == data);
456 }
457 
458 /* ====================================================================
459 
460  Resizable Hash Table Routines
461 
462  We use hash tables in various places, and don't want to have to fix
463  their size ahead of time. These routines provide hash tables that
464  are dynamically resized as items are added and removed. We use
465  "open hashing" with a hash table whose size is a power of two. We
466  keep track of how many items are in the table. The table grows
467  when # of items >= 2*size, and shrinks when # of items < size/2.
468  To resize a hash table, we rehash every item in it.
469 
470  Each item must be a structure whose first field is reserved for use
471  by these hash table routines--it points to the next item in the hash
472  bucket. Hash tables are created and initialized via make_hash_table();
473  you give it a hash function (i.e., a C function) that finds the hash
474  value for a given item, hashing it to a value num_bits wide. For aid
475  in this, we provide masks_for_n_low_order_bits[] that select out the
476  low-order bits of a value: (x & masks_for_n_low_order_bits[23]) picks
477  out the 23 low-order bits of x.
478 
479  Items are added/removed from a hash table via add_to_hash_table() and
480  remove_from_hash_table(). These calls resize the hash table if
481  necessary.
482 
483  The contents of a hash table (or one bucket in the table) can be
484  retrieved via do_for_all_items_in_hash_table() and
485  do_for_all_items_in_hash_bucket(). Each uses a callback function,
486  invoking it with each successive item. The callback function should
487  normally return FALSE. If the callback function ever returns TRUE,
488  iteration over the hash table items stops and the do_for_xxx()
489  routine returns immediately.
490 ==================================================================== */
491 
493  0x00000001, 0x00000003, 0x00000007, 0x0000000F,
494  0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
495  0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
496  0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
497  0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,
498  0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,
499  0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,
500  0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF };
501 
503  hash_function h) {
504  hash_table *ht;
505 
506  ht = static_cast<hash_table_struct *>(allocate_memory (thisAgent, sizeof(hash_table),
508  ht->count = 0;
509  if (minimum_log2size < 1) minimum_log2size = 1;
510  ht->size = static_cast<uint32_t>(1) << minimum_log2size;
513  ht->buckets = static_cast<item_in_hash_table_struct **>(allocate_memory_and_zerofill (thisAgent, ht->size * sizeof(char *),
515  ht->h = h;
516  return ht;
517 }
518 
519 void resize_hash_table (agent* thisAgent, hash_table *ht, short new_log2size) {
520  uint32_t i;
521  bucket_array *new_buckets;
522  item_in_hash_table *item, *next;
523  uint32_t hash_value;
524  uint32_t new_size;
525 
526  new_size = static_cast<uint32_t>(1) << new_log2size;
527  new_buckets =
528  (bucket_array *) allocate_memory_and_zerofill (thisAgent, new_size*sizeof(char *),
530 
531  for (i=0; i < ht->size; i++) {
532  for (item = *(ht->buckets + i); item!=NIL; item=next) {
533  next = item->next;
534  /* --- insert item into new buckets --- */
535  hash_value = (*(ht->h))(item,new_log2size);
536  item->next = *(new_buckets+hash_value);
537  *(new_buckets+hash_value) = item;
538  }
539  }
540 
541  free_memory (thisAgent, ht->buckets, HASH_TABLE_MEM_USAGE);
542  ht->buckets = new_buckets;
543  ht->size = new_size;
544  ht->log2size = new_log2size;
545 }
546 
547 /* RPM 6/09 */
548 void free_hash_table(agent* thisAgent, struct hash_table_struct *ht) {
549  free_memory(thisAgent, ht->buckets, HASH_TABLE_MEM_USAGE);
550  free_memory(thisAgent, ht, HASH_TABLE_MEM_USAGE);
551 }
552 
553 void remove_from_hash_table (agent* thisAgent, struct hash_table_struct *ht,
554  void *item) {
555  uint32_t hash_value;
556  item_in_hash_table *this_one, *prev;
557 
558  this_one = static_cast<item_in_hash_table_struct *>(item);
559  hash_value = (*(ht->h))(item, ht->log2size);
560  if (*(ht->buckets+hash_value)==this_one) {
561  /* --- hs is the first one on the list for the bucket --- */
562  *(ht->buckets+hash_value) = this_one->next;
563  } else {
564  /* --- hs is not the first one on the list, so find its predecessor --- */
565  prev = *(ht->buckets+hash_value);
566  while (prev && prev->next != this_one) prev=prev->next;
567  if ( !prev ) {
568  /* Reaching here means that we couldn't find this_one item */
569  assert(prev && "Couldn't find item to remove from hash table!");
570  return;
571  }
572  prev->next = this_one->next;
573  }
574  this_one->next = NIL; /* just for safety */
575  /* --- update count and possibly resize the table --- */
576  ht->count--;
577  if ((ht->count < ht->size/2) && (ht->log2size > ht->minimum_log2size))
578  resize_hash_table (thisAgent, ht, ht->log2size-1);
579 }
580 
581 void add_to_hash_table (agent* thisAgent, struct hash_table_struct *ht,
582  void *item) {
583  uint32_t hash_value;
584  item_in_hash_table *this_one;
585 
586  this_one = static_cast<item_in_hash_table_struct *>(item);
587  ht->count++;
588  if (ht->count >= ht->size*2)
589  resize_hash_table (thisAgent, ht, ht->log2size+1);
590  hash_value = (*(ht->h))(item, ht->log2size);
591  this_one->next = *(ht->buckets+hash_value);
592  *(ht->buckets+hash_value) = this_one;
593 }
594 
596  struct hash_table_struct *ht,
598  void* userdata)
599 {
600  uint32_t hash_value;
601  item_in_hash_table *item;
602 
603  for (hash_value=0; hash_value < ht->size; hash_value++) {
604  item = (item_in_hash_table *) (*(ht->buckets + hash_value));
605  for ( ; item!=NIL; item = item->next)
606  if ((*f)(thisAgent, item, userdata)) return;
607  }
608 }
609 
612  uint32_t hash_value) {
613  item_in_hash_table *item;
614 
615  hash_value = hash_value & masks_for_n_low_order_bits[ht->log2size];
616  item = (item_in_hash_table *) (*(ht->buckets + hash_value));
617  for ( ; item!=NIL; item = item->next)
618  if ((*f)(item)) return;
619 }
620 
621 /* ====================================================================
622 
623  Module Initialization
624 
625  This routine should be called before anything else in this file
626  is used.
627 ==================================================================== */
628 
629 void init_memory_utilities (agent* thisAgent) {
630  int i;
631 
632  init_memory_pool (thisAgent, &thisAgent->cons_cell_pool, sizeof(cons), "cons cell");
633  init_memory_pool (thisAgent, &thisAgent->dl_cons_pool, sizeof(dl_cons), "dl cons");
634  for (i=0; i<NUM_MEM_USAGE_CODES; i++) thisAgent->memory_for_usage[i] = 0;
635 }
636