Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

mem.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  mem.c
00004  *
00005  * ====================================================================
00006  * Memory allocation and deallocation, string, linked list, and resizable
00007  * hash table utilities for Soar 6.  see soarkernel.h for more comments
00008  * Init_memory_utilities() should be called before any of these routines
00009  * are used.
00010  * =======================================================================
00011  *
00012  * Copyright 1995-2003 Carnegie Mellon University,
00013  *                                                                               University of Michigan,
00014  *                                                                               University of Southern California/Information
00015  *                                                                               Sciences Institute. All rights reserved.
00016  *                                                                              
00017  * Redistribution and use in source and binary forms, with or without
00018  * modification, are permitted provided that the following conditions are met:
00019  *
00020  * 1.   Redistributions of source code must retain the above copyright notice,
00021  *              this list of conditions and the following disclaimer. 
00022  * 2.   Redistributions in binary form must reproduce the above copyright notice,
00023  *              this list of conditions and the following disclaimer in the documentation
00024  *              and/or other materials provided with the distribution. 
00025  *
00026  * THIS SOFTWARE IS PROVIDED BY THE SOAR CONSORTIUM ``AS IS'' AND ANY EXPRESS OR
00027  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00028  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00029  * EVENT SHALL THE SOAR CONSORTIUM  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00030  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00031  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00032  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00033  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00034  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00035  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00036  * The views and conclusions contained in the software and documentation are
00037  * those of the authors and should not be interpreted as representing official
00038  * policies, either expressed or implied, of Carnegie Mellon University, the
00039  * University of Michigan, the University of Southern California/Information
00040  * Sciences Institute, or the Soar consortium.
00041  * =======================================================================
00042  */
00043 
00044 #include "soarkernel.h"
00045 
00046 /* ====================================================================
00047 
00048                    Basic Memory Allocation Utilities
00049 
00050    All memory blocks are allocated via calls to allocate_memory().  It
00051    calls malloc() and aborts if we run out of memory.  Free_memory() is
00052    the inverse of allocate_memory().  Allocate_memory_and_zerofill()
00053    does the obvious thing.  These routines take a usage_code indicating
00054    what purpose the memory is for (hash tables, strings, etc.).  This
00055    is used purely for statistics keeping.
00056 
00057    Print_memory_statistics() prints out stats on the memory usage.
00058 ==================================================================== */
00059 
00060 void *allocate_memory(unsigned long size, int usage_code)
00061 {
00062     char *p;
00063 
00064     current_agent(memory_for_usage)[usage_code] += size;
00065     size += sizeof(char *);
00066     current_agent(memory_for_usage)[STATS_OVERHEAD_MEM_USAGE] += sizeof(char *);
00067 
00068     p = (char *) malloc(size);
00069     if (p == NULL) {
00070         char msg[MESSAGE_SIZE];
00071         snprintf(msg, MESSAGE_SIZE, "\nmem.c: Error:  Tried but failed to allocate %lu bytes of memory.\n", size);
00072         msg[MESSAGE_SIZE - 1] = 0;      /* snprintf doesn't set last char to null if output is truncated */
00073         abort_with_fatal_error(msg);
00074     }
00075     if (((unsigned long) p) & 3) {
00076         char msg[MESSAGE_SIZE];
00077         strncpy(msg, "\nmem.c: Error:  Memory allocator returned an address that's not a multiple of 4.\n",
00078                 MESSAGE_SIZE);
00079         msg[MESSAGE_SIZE - 1] = 0;
00080         abort_with_fatal_error(msg);
00081     }
00082 
00083     fill_with_garbage(p, size);
00084 
00085     *((unsigned long *) p) = size;
00086     p += sizeof(char *);
00087 
00088     return (void *) p;
00089 }
00090 
00091 void *allocate_memory_and_zerofill(unsigned long size, int usage_code)
00092 {
00093     void *p;
00094 
00095     p = allocate_memory(size, usage_code);
00096     memset(p, 0, size);
00097     return p;
00098 }
00099 
00100 void free_memory(void *mem, int usage_code)
00101 {
00102     unsigned long size;
00103 
00104     mem = ((char *) mem) - sizeof(char *);
00105     size = *((unsigned long *) mem);
00106     fill_with_garbage(mem, size);
00107 
00108     current_agent(memory_for_usage)[STATS_OVERHEAD_MEM_USAGE] -= sizeof(char *);
00109     current_agent(memory_for_usage)[usage_code] -= (size - sizeof(char *));
00110 
00111     free(mem);
00112 }
00113 
00114 /* ====================================================================
00115 
00116                           String Utilities
00117 
00118    Make_memory_block_for_string() takes a pointer to a string, allocates
00119    a memory block just large enough to hold the string, and copies the
00120    string into the block.   Free_memory_block_for_string() frees the
00121    block.
00122 
00123    "Growable strings" provide a convenient way of building up a string
00124    piece by piece without having to pre-allocate the right amount of
00125    memory.  To initialize one, say "gs = make_blank_growable_string()"
00126    where "gs" is of type "growable_string".  To concatenate a new string
00127    onto the end of an existing growable string, call
00128    add_to_growable_string(&gs,new_string) [note the "&"].  When you're
00129    done using it, call free_growable_string(gs).  Growable strings are
00130    implemented by allocating a block of memory large enough to hold
00131    (1) the memory block's size, (2) the current string length, and (3)
00132    the current text of the string.  Add_to_growable_string() may result
00133    in a new (larger) memory block being allocated and the text of the 
00134    string being copied.  Three macros provide access to a growable string's
00135    parts:  memsize_of_growable_string(), length_of_growable_string(),
00136    and (most importantly) text_of_growable_string(), which is of type
00137    (char *).
00138 ==================================================================== */
00139 
00140 char *make_memory_block_for_string(const char *s)
00141 {
00142     char *p;
00143     unsigned long size;
00144 
00145     size = strlen(s) + 1;       /* plus one for trailing null character */
00146     p = allocate_memory(size, STRING_MEM_USAGE);
00147     strncpy(p, s, size);
00148     p[size - 1] = 0;
00149     return p;
00150 }
00151 
00152 void free_memory_block_for_string(char *p)
00153 {
00154     free_memory(p, STRING_MEM_USAGE);
00155 }
00156 
00157 #define INITIAL_GROWABLE_STRING_SIZE 100
00158 
00159 growable_string make_blank_growable_string(void)
00160 {
00161     growable_string gs;
00162 
00163     gs = allocate_memory(2 * sizeof(int *) + INITIAL_GROWABLE_STRING_SIZE, STRING_MEM_USAGE);
00164     memsize_of_growable_string(gs) = INITIAL_GROWABLE_STRING_SIZE;
00165     length_of_growable_string(gs) = 0;
00166     *(text_of_growable_string(gs)) = 0;
00167     return gs;
00168 }
00169 
00170 void add_to_growable_string(growable_string * gs, char *string_to_add)
00171 {
00172     int current_length, length_to_add, new_length, new_memsize;
00173     growable_string new;
00174 
00175     current_length = length_of_growable_string(*gs);
00176     length_to_add = strlen(string_to_add);
00177     new_length = current_length + length_to_add;
00178     if (new_length + 1 > memsize_of_growable_string(*gs)) {
00179         new_memsize = memsize_of_growable_string(*gs);
00180         while (new_length + 1 > new_memsize)
00181             new_memsize = new_memsize * 2;
00182         new = allocate_memory(new_memsize + 2 * sizeof(int *), STRING_MEM_USAGE);
00183         memsize_of_growable_string(new) = new_memsize;
00184         strcpy(text_of_growable_string(new), text_of_growable_string(*gs));
00185         free_memory(*gs, STRING_MEM_USAGE);
00186         *gs = new;
00187     }
00188     strcpy(text_of_growable_string(*gs) + current_length, string_to_add);
00189     length_of_growable_string(*gs) = new_length;
00190 }
00191 
00192 void free_growable_string(growable_string gs)
00193 {
00194     free_memory(gs, STRING_MEM_USAGE);
00195 }
00196 
00197 /* ====================================================================
00198 
00199                           Memory Pool Routines
00200 
00201    To allocate and free memory items efficiently at run time, we use
00202    pools of small fixed-size items and do allocation and freeing using
00203    inline macros.  Different memory pools are used for different things
00204    and contain different size items.  Each pool consists of a memory_pool
00205    structure (used for maintaining the pool) and a chain of big blocks
00206    of memory (currently about 32K per block) obtained from allocate_memory().
00207    We maintain a free_list of small items not being used, and allocate by
00208    grabbing the first item on the free list.  If the free list is empty,
00209    we add another big block to the pool.
00210 
00211    Init_memory_pool() should be called to initialize a memory_pool
00212    structure before it is used.  After that, the macro forms [see soarkernel.h]
00213    allocate_with_pool (&mem_pool, &pointer_to_be_set_to_new_item) and
00214    free_with_pool (&mem_pool, pointer_to_item)
00215    are used to allocate and free items.  Print_memory_pool_statistics()
00216    prints stats about the various pools in use and how much memory each
00217    is using.
00218 ==================================================================== */
00219 
00220 #define DEFAULT_INTERLEAVE_FACTOR 1
00221 /* should be 1 for maximum speed, but to avoid a gradual slowdown due
00222    to a gradually decreasing CPU cache hit ratio, make this a larger 
00223    number, must be prime */
00224 #define DEFAULT_BLOCK_SIZE 0x7FF0       /* about 32K bytes per block */
00225 
00226 void add_block_to_memory_pool(memory_pool * p)
00227 {
00228     char *new_block;
00229     unsigned long size, i, item_num, interleave_factor;
00230     char *item, *prev_item;
00231 
00232     /* --- allocate a new block for the pool --- */
00233     size = p->item_size * p->items_per_block + sizeof(char *);
00234     new_block = allocate_memory(size, POOL_MEM_USAGE);
00235     *(char **) new_block = p->first_block;
00236     p->first_block = new_block;
00237     p->num_blocks++;
00238 
00239     /* --- link up the new entries onto the free list --- */
00240     interleave_factor = DEFAULT_INTERLEAVE_FACTOR;
00241     if (interleave_factor >= (unsigned) (p->items_per_block))
00242         interleave_factor = 1;
00243 
00244     item_num = interleave_factor;
00245     prev_item = new_block + sizeof(char *);     /* prev_item is item number 0 */
00246     for (i = 0; i < (unsigned) (p->items_per_block) - 1; i++) {
00247         item = new_block + sizeof(char *) + item_num * p->item_size;
00248         *(char **) prev_item = item;
00249         prev_item = item;
00250         item_num = item_num + interleave_factor;
00251         if (item_num >= (unsigned) (p->items_per_block))
00252             item_num -= p->items_per_block;
00253     }
00254     *(char **) prev_item = p->free_list;
00255     p->free_list = new_block + sizeof(char *);
00256 }
00257 
00258 void init_memory_pool(memory_pool * p, long item_size, char *name)
00259 {
00260     if (item_size < sizeof(char *))
00261         item_size = sizeof(char *);
00262     while (item_size & 3)
00263         item_size++;            /* make sure item_size is multiple of 4 */
00264     p->item_size = item_size;
00265     p->items_per_block = DEFAULT_BLOCK_SIZE / item_size;
00266     p->num_blocks = 0;
00267     p->first_block = NIL;
00268     p->free_list = NIL;
00269 #ifdef MEMORY_POOL_STATS
00270     p->used_count = 0;
00271 #endif
00272     p->next = current_agent(memory_pools_in_use);
00273     current_agent(memory_pools_in_use) = p;
00274     if (strlen(name) > MAX_POOL_NAME_LENGTH) {
00275         char msg[2 * MAX_POOL_NAME_LENGTH];
00276         snprintf(msg, 2 * MAX_POOL_NAME_LENGTH, "mem.c: Internal error: memory pool name too long: %s\n", name);
00277         msg[2 * MAX_POOL_NAME_LENGTH - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
00278         abort_with_fatal_error(msg);
00279     }
00280     strncpy(p->name, name, MAX_POOL_NAME_LENGTH);
00281     p->name[MAX_POOL_NAME_LENGTH - 1] = 0;
00282 
00283 }
00284 
00285 /* ====================================================================
00286 
00287                     Cons Cell and List Utilities
00288 
00289    This provides a simple facility for manipulating generic lists, just
00290    like in Lisp.  A "cons" is a pair of pointers; a "list" is just a cons
00291    (or NIL).  We maintain a memory pool (see above) of conses.
00292    Allocate_cons() is like allocate_with_pool() for conses; free_cons()
00293    is like free_with_pool.  Push(new_item,my_list) is a macro for adding
00294    a new item onto the front of an existing list.
00295 
00296    In addition to the regular conses, we also have a pool of "dl_cons"es--
00297    these are like conses, only doubly-linked.  A "dl_list" is a just a
00298    dl_cons (or NIL).
00299    
00300    Some (consed) list utility functions:  destructively_reverse_list()
00301    does just what it says, and returns a pointer to the new head of the
00302    list (formerly the tail).  Member_of_list() tests membership, using
00303    "==" as the equality predicates.  Add_if_not_member() is like Lisp's
00304    "pushnew"; it returns the new list (possibly unchanged) list.
00305    Free_list() frees all the cons cells in the list.
00306    
00307    Sometimes we need to surgically extract particular elements from a
00308    list.  Extract_list_elements() and extract_dl_list_elements() do this.
00309    They use a callback function that indicates which elements to extract:
00310    the callback function is called on each element of the list, and should
00311    return TRUE for the elements to be extracted.  The two extraction
00312    functions return a list (or dl_list) of the extracted elements.
00313 ==================================================================== */
00314 
00315 cons *destructively_reverse_list(cons * c)
00316 {
00317     cons *prev, *current, *next;
00318 
00319     prev = NIL;
00320     current = c;
00321     while (current) {
00322         next = current->rest;
00323         current->rest = prev;
00324         prev = current;
00325         current = next;
00326     }
00327     return prev;
00328 }
00329 
00330 bool member_of_list(void *item, list * the_list)
00331 {
00332     while (the_list) {
00333         if (the_list->first == item)
00334             return TRUE;
00335         the_list = the_list->rest;
00336     }
00337     return FALSE;
00338 }
00339 
00340 list *add_if_not_member(void *item, list * old_list)
00341 {
00342     cons *c;
00343 
00344     for (c = old_list; c != NIL; c = c->rest)
00345         if (c->first == item)
00346             return old_list;
00347     allocate_cons(&c);
00348     c->first = item;
00349     c->rest = old_list;
00350     return c;
00351 }
00352 
00353 void free_list(list * the_list)
00354 {
00355     cons *c;
00356 
00357     while (the_list) {
00358         c = the_list;
00359         the_list = the_list->rest;
00360         free_cons(c);
00361     }
00362 }
00363 
00364 list *extract_list_elements(list ** header, cons_test_fn f)
00365 {
00366     cons *first_extracted_element, *tail_of_extracted_elements;
00367     cons *c, *prev_c, *next_c;
00368 
00369     first_extracted_element = NIL;
00370     tail_of_extracted_elements = NIL;
00371 
00372     prev_c = NIL;
00373     for (c = (*header); c != NIL; c = next_c) {
00374         next_c = c->rest;
00375         if (!f(c)) {
00376             prev_c = c;
00377             continue;
00378         }
00379         if (prev_c)
00380             prev_c->rest = next_c;
00381         else
00382             *header = next_c;
00383         if (first_extracted_element)
00384             tail_of_extracted_elements->rest = c;
00385         else
00386             first_extracted_element = c;
00387         tail_of_extracted_elements = c;
00388     }
00389     if (first_extracted_element)
00390         tail_of_extracted_elements->rest = NIL;
00391     return first_extracted_element;
00392 }
00393 
00394 dl_list *extract_dl_list_elements(dl_list ** header, dl_cons_test_fn f)
00395 {
00396     dl_cons *first_extracted_element, *tail_of_extracted_elements;
00397     dl_cons *dc, *next_dc;
00398 
00399     first_extracted_element = NIL;
00400     tail_of_extracted_elements = NIL;
00401 
00402     for (dc = (*header); dc != NIL; dc = next_dc) {
00403         next_dc = dc->next;
00404         if (!f(dc))
00405             continue;
00406         remove_from_dll((*header), dc, next, prev);
00407         if (first_extracted_element)
00408             tail_of_extracted_elements->next = dc;
00409         else
00410             first_extracted_element = dc;
00411         dc->prev = tail_of_extracted_elements;
00412         tail_of_extracted_elements = dc;
00413     }
00414     if (first_extracted_element)
00415         tail_of_extracted_elements->next = NIL;
00416     return first_extracted_element;
00417 }
00418 
00419 /* ====================================================================
00420 
00421                    Resizable Hash Table Routines
00422 
00423    We use hash tables in various places, and don't want to have to fix
00424    their size ahead of time.  These routines provide hash tables that
00425    are dynamically resized as items are added and removed.  We use
00426    "open hashing" with a hash table whose size is a power of two.  We
00427    keep track of how many items are in the table.  The table grows
00428    when # of items >= 2*size, and shrinks when # of items < size/2.
00429    To resize a hash table, we rehash every item in it.
00430 
00431    Each item must be a structure whose first field is reserved for use
00432    by these hash table routines--it points to the next item in the hash
00433    bucket.  Hash tables are created and initialized via make_hash_table();
00434    you give it a hash function (i.e., a C function) that finds the hash
00435    value for a given item, hashing it to a value num_bits wide.  For aid
00436    in this, we provide masks_for_n_low_order_bits[] that select out the
00437    low-order bits of a value:  (x & masks_for_n_low_order_bits[23]) picks
00438    out the 23 low-order bits of x.
00439 
00440    Items are added/removed from a hash table via add_to_hash_table() and
00441    remove_from_hash_table().  These calls resize the hash table if
00442    necessary.
00443 
00444    The contents of a hash table (or one bucket in the table) can be
00445    retrieved via do_for_all_items_in_hash_table() and
00446    do_for_all_items_in_hash_bucket().  Each uses a callback function,
00447    invoking it with each successive item.  The callback function should
00448    normally return FALSE.  If the callback function ever returns TRUE,
00449    iteration over the hash table items stops and the do_for_xxx()
00450    routine returns immediately.  
00451 ==================================================================== */
00452 
00453 unsigned long masks_for_n_low_order_bits[33] = { 0x00000000,
00454     0x00000001, 0x00000003, 0x00000007, 0x0000000F,
00455     0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
00456     0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
00457     0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
00458     0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,
00459     0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,
00460     0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,
00461     0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
00462 };
00463 
00464 struct hash_table_struct *make_hash_table(short minimum_log2size, hash_function h)
00465 {
00466     hash_table *ht;
00467 
00468     ht = allocate_memory(sizeof(hash_table), HASH_TABLE_MEM_USAGE);
00469     ht->count = 0;
00470     if (minimum_log2size < 1)
00471         minimum_log2size = 1;
00472     ht->size = (((unsigned long) 1) << minimum_log2size);
00473     ht->log2size = minimum_log2size;
00474     ht->minimum_log2size = minimum_log2size;
00475     ht->buckets = allocate_memory_and_zerofill(ht->size * sizeof(char *), HASH_TABLE_MEM_USAGE);
00476     ht->h = h;
00477     return ht;
00478 }
00479 
00480 void resize_hash_table(hash_table * ht, short new_log2size)
00481 {
00482     unsigned long i;
00483     bucket_array *new_buckets;
00484     item_in_hash_table *item, *next;
00485     unsigned long hash_value;
00486     unsigned long new_size;
00487 
00488     new_size = (((unsigned long) 1) << new_log2size);
00489     new_buckets = (bucket_array *) allocate_memory_and_zerofill(new_size * sizeof(char *), HASH_TABLE_MEM_USAGE);
00490 
00491     for (i = 0; i < ht->size; i++) {
00492         for (item = *(ht->buckets + i); item != NIL; item = next) {
00493             next = item->next;
00494             /* --- insert item into new buckets --- */
00495             hash_value = (*(ht->h)) (item, new_log2size);
00496             item->next = *(new_buckets + hash_value);
00497             *(new_buckets + hash_value) = item;
00498         }
00499     }
00500 
00501     free_memory(ht->buckets, HASH_TABLE_MEM_USAGE);
00502     ht->buckets = new_buckets;
00503     ht->size = new_size;
00504     ht->log2size = new_log2size;
00505 }
00506 
00507 void remove_from_hash_table(struct hash_table_struct *ht, void *item)
00508 {
00509     unsigned long hash_value;
00510     item_in_hash_table *this_one, *prev;
00511 
00512     this_one = item;
00513     hash_value = (*(ht->h)) (item, ht->log2size);
00514     if (*(ht->buckets + hash_value) == this_one) {
00515         /* --- hs is the first one on the list for the bucket --- */
00516         *(ht->buckets + hash_value) = this_one->next;
00517     } else {
00518         /* --- hs is not the first one on the list, so find its predecessor --- */
00519         prev = *(ht->buckets + hash_value);
00520         while (prev->next != this_one)
00521             prev = prev->next;
00522         prev->next = this_one->next;
00523     }
00524     this_one->next = NIL;       /* just for safety */
00525     /* --- update count and possibly resize the table --- */
00526     ht->count--;
00527     if ((ht->count < ht->size / 2) && (ht->log2size > ht->minimum_log2size))
00528         resize_hash_table(ht, (short) (ht->log2size - 1));
00529 }
00530 
00531 void add_to_hash_table(struct hash_table_struct *ht, void *item)
00532 {
00533     unsigned long hash_value;
00534     item_in_hash_table *this_one;
00535 
00536     this_one = item;
00537     ht->count++;
00538     if (ht->count >= ht->size * 2)
00539         resize_hash_table(ht, (short) (ht->log2size + 1));
00540     hash_value = (*(ht->h)) (item, ht->log2size);
00541     this_one->next = *(ht->buckets + hash_value);
00542     *(ht->buckets + hash_value) = this_one;
00543 }
00544 
00545 void do_for_all_items_in_hash_table(struct hash_table_struct *ht, hash_table_callback_fn f)
00546 {
00547     unsigned long hash_value;
00548     item_in_hash_table *item;
00549 
00550     for (hash_value = 0; hash_value < ht->size; hash_value++) {
00551         item = (item_in_hash_table *) (*(ht->buckets + hash_value));
00552         for (; item != NIL; item = item->next)
00553             if ((*f) (item))
00554                 return;
00555     }
00556 }
00557 
00558 void do_for_all_items_in_hash_bucket(struct hash_table_struct *ht, hash_table_callback_fn f, unsigned long hash_value)
00559 {
00560     item_in_hash_table *item;
00561 
00562     hash_value = hash_value & masks_for_n_low_order_bits[ht->log2size];
00563     item = (item_in_hash_table *) (*(ht->buckets + hash_value));
00564     for (; item != NIL; item = item->next)
00565         if ((*f) (item))
00566             return;
00567 }
00568 
00569 /* ====================================================================
00570 
00571                        Module Initialization
00572 
00573    This routine should be called before anything else in this file
00574    is used.
00575 ==================================================================== */
00576 
00577 void init_memory_utilities(void)
00578 {
00579     int i;
00580 
00581     init_memory_pool(&current_agent(cons_cell_pool), sizeof(cons), "cons cell");
00582     init_memory_pool(&current_agent(dl_cons_pool), sizeof(dl_cons), "dl cons");
00583     for (i = 0; i < NUM_MEM_USAGE_CODES; i++)
00584         current_agent(memory_for_usage)[i] = 0;
00585 }

Generated on Thu Dec 11 13:00:17 2003 for Soar Kernel by doxygen 1.3.5