/* * File: gcalloc.h * Last modified on Sat Aug 28 10:17:26 1993 by eroberts * ----------------------------------------------------- * This file defines the interface for a garbage-collecting * allocator. This interface defines only an initialization * function (InitGCAllocator) and facilities for protecting * global variables. The actual allocation is handled by * the standard GetBlock and FreeBlock functions defined in * genlib.h. These functions use the garbage-collecting * allocator if InitGCAllocator has been called. */ #ifndef _gcalloc_h #define _gcalloc_h #include #include "genlib.h" /* * Background * ---------- * In a typical application program, memory is allocated in * three different regions. Static data is stored in fixed * memory addresses and is declared in C using the storage * classes static or extern (extern is in fact omitted in * the defining instance). Automatic data (the default * storage class for variables defined within a function) * is stored on the control stack, where it is allocated * when a function is called and deallocated on return * from that function. In addition to these two classes, * however, many programs will need to allocated additional * memory from an expandable pool called the "heap." All * memory in the heap is addressed via pointers, which are * generated as the program runs. * * Although heap allocation is extremely convenient, * particularly when used in library implementations, it * suffers from an important failing. The problem is not * identifying what parts of the program have responsibility * for allocating memory. That task is easy. The thorny * issue is who has responsibility for freeing previously * allocated memory when it is no longer needed. * * The central problem is that the operations of allocating * and freeing memory are most naturally situated on opposite * sides of the interface boundary between an implementation * and its client. The implementation knows when to allocate * memory and simply returns pointers to the client. The * implementation, however, has no idea when the client is * finished with the allocated object, so that freeing the * storage has to remain the client's responsibility, even * though the client may not understand enough about the * object's structure to do so. * * To get around this problem, most libraries have been * designed in one of two ways. One approach is to make * the client responsible for allocating storage and * passing a pointer to the implementation. The alternative * is to provide procedures in each interface that free any * storage allocated by the abstraction. Each of these * approaches is decidedly non-optimal. * * A completely different alternative is to use a strategy * called "garbage collection." Under this strategy, * implementations are free to allocate memory and pass * their addresses back to the client. As long as the * client holds that pointer, the memory is considered * to be in use. When the pointer goes away (which occurs * when a different pointer is assigned to the variable or * when the variable holding the pointer is deallocated * by returning from the function declaring it), the * storage to which it referred is no longer accessible, * and can be reallocated to new purposes. * * Detecting what memory is garbage does not happen as the * memory becomes inaccessible. Instead, the traditional * garbage-collection strategy is to wait until a memory * cannot be satisfied, and then to go through accessible * memory to see what previously allocated blocks are no * longer reachable, freeing these for reuse. */ /* * Client responsibilities * ----------------------- * The garbage-collecting allocator imposes some important * responsibilities on the client who uses the package. In * particular, the client must agree to obey the following * rules: * * (1) Whenever a heap object is allocated, the client * must agree to keep at least one copy of the * original pointer returned by GetBlock. In C, * it is common to use pointer arithmetic to adjust * a pointer so that, for example, it points to the * interior of an array. The rule is that a block * is protected from garbage collection only by * a pointer to its beginning and not to its * interior. Note that this rule does not prohibit * pointing to the interior of the block; the * implication is simply that the client must * also keep the original pointer around. * * (2) The garbage collector must be able to identify * all places in which valid heap pointers might * be maintained, so that it can ensure that * valid data is not collected by accident. This * package (subject to the system assumptions * below) is able to find pointers living in * automatic variables on the stack, or in heap * blocks allocated by GetBlock which themselves * remain accessible (see condition 3 as well). If * static variables contain allocated data, however, * the client must warn the allocator of this * fact by calling * * ProtectVariable(variable); * * where variable is the name of the global * variable. * * (3) It is the client's responsibility to free * explicitly any storage allocated outside of the * GetBlock/FreeBlock mechanism. Moreover, it is * also important to remember that the garbage * collector is not able to find pointers which are * stored in dynamically allocated blocks unless * those blocks were allocated by GetBlock. Thus, * if an implementation makes direct calls to the * system allocation functions in addition to * GetBlock, that implementation may not store the * GetBlock pointers in the system-allocated memory. */ /* * Function: InitGCAllocator * Usage: InitGCAllocator(&argc); * ------------------------------ * This call enables the garbage-collecting allocator and * also establishes the base of the stack for tracing. This * call should be made from main, and the address passed * should be the address of the argc parameter. */ void InitGCAllocator(void *stackbase); /* * Macro: ProtectVariable * Usage: ProtectVariable(v); * -------------------------- * Registers a global variable with the allocation system, so * that the variable is traced when the garbage collector is * used. This operation needs to be provided in "genlib.h" * so that code can be written to function correctly whether * or not the garbage-collecting allocator is loaded. */ #define ProtectVariable(v) ProtectBlock(&v, sizeof v) /* * Function: ProtectBlock * Usage: ProtectBlock(ptr, nbytes); * --------------------------------- * This function is not usually called by clients (who will * ordinarily use ProtectVariable instead), but has the * effect of protecting the block of memory beginning at * ptr and extending for nbytes from the garbage collector. */ void ProtectBlock(void *ptr, size_t nbytes); /* * Global linkage variable: _acb * ----------------------------- * This variable is used to hold the allocation control block * that provides the linkage between this package and the * dynamic allocator. The reason for using the structure * as a linkage is so that the garbage-collecting allocator * need not even be loaded if it is not explicitly called. */ typedef struct { void *(*allocMethod)(size_t nbytes); void (*freeMethod)(void *ptr); void (*protectMethod)(void *ptr, size_t nbytes); } *_GCControlBlock; extern _GCControlBlock _acb; #endif