/* * This software and its associated documentation are protected by * Copyright 1995 Geodesic Systems Inc. All Rights Reserved. * Portions of the software include modification to code which was * released publicly by Xerox Corporation, subject to the requirement that * the following notice be retained and included with the modified code: * "Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers. * Copyright (c) 1991-1995 by Xerox Corporation. * All rights reserved. THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY * NO WARRANTY EXPRESS OR IMPLIED. ANY USE IS AT YOUR OWN RISK." */ /* Fiterman, May 8, 1997 14:20 am CST */ #ifndef _GCT_H # define _GCT_H # ifdef __cplusplus extern "C" { # endif # include # include #define NO_PARAMS void #ifdef __cplusplus # undef NO_PARAMS #endif #ifndef NO_PARAMS # define NO_PARAMS #endif #ifdef GC_BUILD_DLL # define GC_SYS_IMPORT __declspec(dllimport) # define GC_IMPORTX __declspec(dllexport) # define GC_EXPORT __declspec(dllexport) # ifdef GC_CRTDLL # define GC_EXPORTY(t) __declspec(dllexport) t # else # define GC_EXPORTY(t) t # endif # define GC_EXPORTX(t) GC_EXPORTY(t) __cdecl #else /* GC_BUILD_DLL */ # define GC_IMPORTX __declspec(dllimport) # define GC_EXPORTX(t) __declspec(dllimport) t # ifdef __cplusplus # define gcInitFunction extern "C" __declspec(dllexport) void __cdecl # else # define gcInitFunction __declspec(dllexport) void # endif # define GC_EXPORTY(x) x #endif /* GC_BUILD_DLL */ #define GC_IMPORT extern GC_IMPORTX /* * Define word and signed_word to be unsigned and signed types of the * size as char * or void *. There seems to be no way to do this * even semi-portably. The following is probably no better/worse * than almost anything else * The ANSI standard suggests that size_t and ptr_diff_t might be * better choices. But those appear to have incorrect definitions * on may systems. Notably "typedef int size_t" seems to be both * frequent and WRONG */ typedef unsigned long gcWord; typedef long gcSignedWord; /* Public read-only variables */ GC_IMPORT gcWord gcCollections; /* Counter incremented per collection. */ /* Public R/W variables */ /* * Non zero Enables logging output. This involves a performance * cost and is thus not the default. */ GC_IMPORT long gcPrintStats; /* * Print memory usage statistics */ GC_IMPORT int gcPrintMemUsage; /* * Consider as roots all non heap mappings where pointers can be found * Sunos only */ GC_IMPORT int gcScanAllPotentialRoots; /* Disable signals in critical sections of the collector */ GC_IMPORT int gcDisableSignalsSwitch; /* Memory explicitly freed before next footprint-reduce */ GC_IMPORT unsigned long gcMaxMemFreedBeforeNextFootPrintReduce; /* * Some applications like netscape with java change the location of * the execution stack (Argh!). This causes the collector to smash memory when * cleaning the stack. When this variable is set to 0, the stack is cleaned * just a little above the current sp. This prevents netscape with java from * crashing. */ GC_IMPORT int gcAllowUserStacks; /* * Memory usage statistics are obtained at the end of a garbage collection * This variable forces a collection every so many bytes allocated. * The initialy value is 1 Mb and it can be changed at run time with * an environment var. */ GC_IMPORT unsigned long gcBytesBeforeNextStatistics; /* * Non zero enables collect at end. Defaults to 1 for report * libs and zero otherwise. */ GC_IMPORT int gcCollectAtEnd; /* * Defaults to zero which implies not flushing the log file. * If gcFlushLog > 0 flush the log file every gcFlushLog lines. */ GC_IMPORT int gcFlushLog; /* * Normally non leaf objects are zeroed to avoid spotting pointers * in them when they are reallocated. Setting this to zero prevents * that and may speed up some programs. */ GC_IMPORT int gcZeroAllocatedObject; /* * Non zero enables free(). zero causes free() to be ignored. * defaults to one. Not used in the report libraries. */ GC_IMPORT char gcEnableFree; /* * gcFixPrematureFrees() sets gcEnableFree to 0; and other * tuning stuff. Fixing premature frees may increase memory * usage. gcStopFixingPrematureFrees(NO_PARAMS) has the opposite * effect. Both functions return nonzero if premature frees * were previously being fixed. */ GC_EXPORTX(int) gcFixPrematureFrees(NO_PARAMS); GC_EXPORTX(int) gcStopFixingPrematureFrees(NO_PARAMS); /* * If gcEnableFree is zero and gcFreeProcessOldObject is non * zero it will be called to process the free()ed object. * gcFreeProcessOldObject defaults to zero. gcDefaultFreeProcessOldObject * will zero old objects which seems a reasonable. */ typedef void (* gcObjectFunction)(void *obj, size_t size); GC_EXPORTX(void) gcDefaultFreeProcessOldObject(void *obj, size_t size); GC_IMPORT gcObjectFunction gcFreeProcessOldObject; /* * This points to the name of the log file. It is initially aimed at * "gc.log", it is used to create the log file the first time output * is done, after that it is never used. This constant is contained * in a separate module so it can be replaced before startup. */ typedef const char * gcConstCharStar; GC_IMPORT gcConstCharStar gcLogFile; /* * Activates stack tracing on the position where items are allocated. * Only used when linked to a debug library. */ GC_IMPORT int gcShowStackTrace; /* * When an attempted allocation fails, Great Circle must decide whether to * collect or expand the heap. gcNotTransparent != 0 says always expand * this until stopped by gcSetMaxHeapSize or a failure to expand. * gcDontExpand != 0 says always collect but then expand if that isn't enough. * * Otherwise collect if (M > N/(gcPriority + 1)) where N is the heap size plus * a rough estimate of the root set size, and M is the amount allocated since * the last complete collection. * * Initially, gcPriority = 3, increasing its value will use less space but * more collection time. Decreasing it will appreciably decrease collection * time at the expense of space. gcPriority = 0 will cause the equation to * always choose expand. Setting incremental mode or pseudo incremental mode * effectivly doubles gcPriority. Since a single paging operation is likely * to eat more time than the collector ever could, increasing the number of * collections has the paradoxical effect of speeding up some programs. * * If you call gcAttemptCollection() the equation (M > N/(gcPriority + 1)) * will determine if a collection actually takes place. */ GC_IMPORT unsigned gcPriority; GC_IMPORT int gcNotTransparent; GC_IMPORT int gcDontExpand; /* * Number of partial collections between full collections. * Matters only if gcIncremental is set. */ GC_IMPORT int gcFullFrequency; #ifdef GC_DEBUG #define GC_DEBUG_BOUNDS_CHECK 1 #define GC_DEBUG_LINE_NUMBERS 1 #define GC_WRAPPED_NEW new(__FILE__, __LINE__) #else #define GC_WRAPPED_NEW new #endif /* Public procedures */ /* * General purpose allocation routines, with malloc calling conventions. * The leaf versions promise that no relevant pointers are contained * in the object. The nonleaf versions guarantee that the new object * is cleared. gcMallocManual allocates an object that is scanned * for pointers to collectible objects, but is not itself collectible. */ GC_EXPORTX(void *) gcMalloc(size_t size_in_bytes); GC_EXPORTX(void *) gcMallocLeaf(size_t size_in_bytes); GC_EXPORTX(void *) gcMallocManual(size_t size_in_bytes); /* * Explicitly deallocate an object. Dangerous if used incorrectly. * Requires a pointer to the base of an object. * An object should not be enabled for finalization when it is * explicitly deallocated. * gcFree(0) is a no-op, as required by ANSI C for free. */ GC_EXPORTX(void) gcFree(void * object_addr); /* * gcMallocIgnoreOffPage reduces the chance of accidentally retaining * a large, > 4096 byte, object as a result of scanning an integer * that happens to be an address inside the array. Large arrays usually * are only used where there is a pointer to the beginning of the array. * * This was built around the needs of very large Xwindows programs, we * have discovered that it works well with almost all programs, improving * space and speed efficiency. * * gcMallocIgnoreOffPage(lb) acts like malloc(lb) except that only pointers * to the first 4096 bytes will be used by the collector to keep the * object alive. If lb is smaller than 4K it acts exactly like malloc. * * gcMallocIgnoreOffPage is the connection for malloc(lb) where * lb > gcVeryLargeAllocationSize && gcIgnoreOffPage * * gcVeryLargeAllocationSize is initialized to 100000. * * gcIgnoreOffPage is in a separate module in the libraries to * allow you to replace it in programs where you have no source code, or * where you need it replaced before startup code runs. This also effects * calloc and realloc. It is initialized to 1 except in the gcsome and * gcsomedb libraries where it is initialized to 0. * * If we need a block N bytes long and have a block > N + gcBlackSizeLimit * and N > gcBlackSizeLimit but all possible positions in it are * the targets of apparent pointers, we use it anyway and print a warning. * This risks leaking the block due to a false reference. But not using * it risks unreasonable immeadiate heap growth. gcBlackSizeLimit defaults * to 100K. */ GC_EXPORTX(void *) gcMallocIgnoreOffPage(size_t lb); GC_EXPORTX(void *) gcMallocLeafIgnoreOffPage(size_t lb); GC_IMPORT unsigned long gcVeryLargeAllocationSize; GC_IMPORT int gcIgnoreOffPage; GC_IMPORT long gcBlackSizeLimit; /* Kinds of objects */ # define gcLeafObject 0 # define gcCollectibleObject 1 # define gcManualObject 2 /* * Return the kind of an object, gcLeafObject etc. */ GC_EXPORTX(int) gcWhatKind(void *p); /* * Return a pointer to the base (lowest address) of an object given * a pointer to a location within the object * Return 0 if displaced_pointer doesn't point to within a valid object. * gcIsValidPointer returns the start of the users area. * gcBase may point to debug information if present. */ GC_EXPORTX(void *) gcIsValidPointer(const void * displaced_pointer); GC_EXPORTX(void *) gcBase(const void * displaced_pointer); /* * Check object with debugging info. Return kinds of * damage found as bit flags. * 1 User size smashed * 2 Start Flag smashed * 4 Near End flag smashed * 8 Far End flag smashed * 16 Previously freed. * 32 not a collectible object * 64 too small for a debug object od debug lib not used */ GC_EXPORTX(int) gcObjectCheck(const void *ohdr); /* * Given a pointer into an object, return its size in bytes. gcFullSize * includes the debug header. */ GC_EXPORTX(size_t) gcSize(const void * object_addr); GC_EXPORTX(size_t) gcFullSize(const void * object_addr); /* * A realloc()'ed object has the same collection kind as the original */ GC_EXPORTX(void *) gcRealloc(void * old_object, size_t new_size_in_bytes); /* * Logging and diagnostic output. * * gcPrintf, gcErrorPrintf, gcWarningPrintf, gcAbort, gcReportLeak, * gcAbortPrintf and gcLogFileAbort all point at functions used for * various kinds of logging. The are initialized to point to functions * who`s names are gcDefaultPrintf, gcDefaultErrorPrintf etc. * * gcDefaultPrintf and gcDefaultErrorPrintf both print to the log file. * * gcDefaultWarningPrintf() only prints to the logfile if gcPrintStats * is nonzero. Otherwise it does nothing. * * gcDefaultAbortPrintf() formats a line and calls gcAbort. * * gcDefaultAbort() prints and exits. * * gcReportLeak() is used by the gcreport and gcreptdb libs to report * leaks. It's work is primarily done by gcPrintObj(). * * If gcLogAllLeaks is nonzero, all leaks are reported to the log file. * If nonzero, only the first gcMaximumLeaksToLogFile are printed. * * void gcPuts() invokes via the function pointer gcPutsFunction which * defaults to gcDefaultPuts. In the threads libraries it locks and unlocks * the log file to prevent chaos. gcDefaultPuts discards calls made before * the collector is initialized. * * void gcDefaultPuts(const char *msg); writes to gcLogFile and insures it * is flushed every gcFlushLog lines. It is used by the above logging * functions. It may call gcLogFileAbort() which reports failures in using * gcLogFile and then aborts, hopefully a rare event. In GUI environment and * other situations, the user may wish to replace this. The function of * gcDefaultLogFileAbort is * sprintf(buf, "%s of %s failed.\n", msg, gcLogFile); * display buf somehow with the log file dead. * abort(); */ typedef void (* gcPrintFunction)(const char *, ...); typedef void (* gcPutFunction)(const char *msg); GC_EXPORTX(void) gcDefaultPrintf(const char *, ...); GC_EXPORTX(void) gcDefaultAbortPrintf(const char *, ...); GC_EXPORTX(void) gcDefaultWarningPrintf(const char *, ...); GC_EXPORTX(void) gcDefaultErrorPrintf(const char *, ...); GC_EXPORTX(void) gcDefaultAbort(const char *msg); GC_EXPORTX(void) gcPuts(const char *msg); GC_EXPORTX(void) gcDefaultPuts(const char *msg); GC_EXPORTX(void) gcDefaultLogFileAbort(const char *msg); GC_EXPORTX(void) gcDefaultReportLeak(const char *msg); GC_IMPORT gcPrintFunction gcPrintf, gcAbortPrintf, gcWarningPrintf, gcErrorPrintf; GC_IMPORT gcPutFunction gcAbort, gcLogFileAbort, gcReportLeak, gcPutsFunction; GC_IMPORT int gcLogAllLeaks; GC_IMPORT unsigned gcMaximumLeaksToLogFile; GC_IMPORT int gcGUIEnabled; /* * p points to somewhere inside an object. * Print a human readable description of the object using gcPrintf. If the * object has debugging information, this will all be printed as well. */ GC_EXPORTX(void) gcPrintObject(const void *p); /* * Returns the debugging information */ GC_EXPORTX(char *) gcDebugString(const void *p); GC_EXPORTX(int) gcDebugInt(const void *p); /* * Explicitly increase the heap size. Returns 0 on failure, 1 on success. * If you can use this to set your heap size to near its final value * your program will run more efficiently due to fewer collection cycles * and more efficient data structures. gcLogFile will show log when collector * expands the heap and how much. You may want to use gcNotTransparent * and gcSetMaxHeapSize() instead. */ GC_EXPORTX(int) gcExpandHeap(size_t number_of_bytes); /* * Limit the heap size to n bytes. Useful when you're debugging * especially on systems that don't handle running out of memory well * n == 0 ==> unbounded. This is the default */ GC_EXPORTX(void) gcSetMaxHeapSize(long n); /* * gcClearRoots clears the set of root segments. * gcAddRoots Adds a root segment. * gcDeclareLeafRoot declares all or part of a root segment as leaf. * All for wizards only. */ GC_EXPORTX(void) gcClearRoots(NO_PARAMS); GC_EXPORTX(void) gcAddRoots(const char *low_address, const char *high_address_plus_1); GC_EXPORTX(void) gcDeclareLeafRoot(const char *low_address, const char *high_address_plus_1); /* Explicitly trigger a full, world-stop collection. */ GC_EXPORTX(void) __cdecl gcCollect(NO_PARAMS); /* * Trigger a full world-stopped collection. Abort the collection if * and when stop_func returns a nonzero value. gcIdleTest will be * called frequently, and should be reasonably fast. This works even * if virtual dirty bits, and hence incremental collection is not * available for this architecture. Collections can be aborted faster * than normal pause times for incremental collection. However, * aborted collections do no useful work; the next collection needs * to start from the beginning. */ typedef int (* gcIdleTestFunction)(NO_PARAMS); GC_EXPORTX(int) gcAttemptCollection(NO_PARAMS); /* * Explicitly trigger a full world-stop collection followed by * an explicit foot print reduce, or attempt to do so. Footprint * reduce wont free memory used since the last footprint reduce. * Normally this is correct but to do an extreme job call twice. */ GC_EXPORTX(void) gcFootPrintReduce(NO_PARAMS); GC_EXPORTX(int) gcAttemptFootPrintReduce(NO_PARAMS); /* * In windows mode this defaults to gcMSWinIdleTest otherwise it * starts null and must be aimed by the user. Only used by gcAttemptCollection * and gcEnablePseudoIncremental. */ GC_IMPORT gcIdleTestFunction gcIdleTest; /* * In Pseudo incremental mode Great Circle will periodically call * gcAttemptCollection(); * in an attempt to free storage before increasing allocated heap. * There is actually a complex heuristic involved using the amount * allocated since the last collection and a few other things. */ GC_EXPORTX(int) gcEnablePseudoIncremental(NO_PARAMS); GC_EXPORTX(int) gcDisablePseudoIncremental(NO_PARAMS); /* * gcNeverStopFunc can be used as a gcIdleTestFunction, gcAttemptCollection * recongizes it and is extra efficient. */ GC_EXPORTX(int) gcNeverStopFunc(NO_PARAMS); /* * gcMSWinIdleTest is an idle test designed for the Windows environment. * It returns a 1 if there are windows events to process. */ GC_EXPORTX(int) gcMSWinIdleTest(NO_PARAMS); /* * Return the number of bytes in the heap. Excludes collector private * data structures. Includes empty blocks and fragmentation loss. */ GC_EXPORTX(size_t) gcGetHeapSize(NO_PARAMS); /* Return the number of bytes allocated since the last collection. */ GC_EXPORTX(size_t) gcGetBytesSinceGc(NO_PARAMS); /* * Enable incremental/generational collection. * Don't use in leak finding mode. */ GC_EXPORTX(void) gcEnableIncremental(NO_PARAMS); /* * Perform some garbage collection work, if appropriate. * Return 0 if there is no more work to be done. * Typically performs an amount of work corresponding roughly * to marking from one page. Does nothing if incremental collection is * disabled. It is reasonable to call this in a wait loop * until it returns 0. Returns 0 if not in incremental mode. */ GC_EXPORTX(int) gcMinWork(NO_PARAMS); /* * This sets the scan alignment. gcSetScanAlignment(4) says all pointers * will be found on a 4 boundary. gcSetScanAlignment(1) says pointers may * be on any byte boundary. This returns True on success False on failure. * It hopefully defaults to the values used by the compiler to align pointers * and should not be reset unless you force pointers to odd boundaries. * This is checked to be 8, 4, 2 or 1 and minamum value. */ GC_EXPORTX(int) gcSetScanAlignment(int align); /* Get current scan Alignment */ GC_EXPORTX(int) gcGetScanAlignment(NO_PARAMS); /* * Debugging (annotated) allocation. gcCollect will check * objects allocated in this way for overwrites, etc. See * #ifdef GC_DEBUG at the end of this file. */ GC_EXPORTX(void *) __cdecl gcMallocDebug(size_t size_in_bytes, const char * descr_string, int descr_int); GC_EXPORTX(void *) __cdecl gcMallocLeafDebug(size_t size_in_bytes, const char * descr_string, int descr_int); GC_EXPORTX(void *) __cdecl gcMallocIgnoreOffPageDebug(size_t size_in_bytes, const char * descr_string, int descr_int); GC_EXPORTX(void *) __cdecl gcMallocLeafIgnoreOffPageDebug(size_t size_in_bytes, const char * descr_string, int descr_int); GC_EXPORTX(void *) __cdecl gcMallocManualDebug(size_t size_in_bytes, const char * descr_string, int descr_int); GC_EXPORTX(void) __cdecl gcFreeDebug(void * object_addr); GC_EXPORTX(void *) __cdecl gcReallocDebug(void * old_object, size_t new_size_in_bytes, const char * descr_string, int descr_int); GC_EXPORTX(void *) __cdecl gcGlobalMallocDebug(size_t size, const char * descr_string, int lineNo); #define gcCalloc(size, num) gcMalloc((size) * (num)) /* * Finalization. gcDeclareFinalizer[Offset] uses an ignore * selfpointers form required by C++. The Offset form is required by C++. * See the C++ section of this file for an example. * gcDeclareFinalizerNoPointers declares the finalized object has no pointers * to other objects that it requires at finalization time. */ typedef void (* gcFinalizationProc)(void * obj); GC_EXPORTX(void) gcRegisterFinalizer(void * obj, gcFinalizationProc fn, void * cd); GC_EXPORTX(void) gcDeclareFinalizer(void * obj, gcFinalizationProc fn); GC_EXPORTX(void) gcDeclareFinalizerNoPointers(void * obj, gcFinalizationProc fn, void * cd); GC_EXPORTX(void) gcDeclareFinalizerOffset(void * obj, gcFinalizationProc fn, void * cd); /* * The following routine may be used to break cycles between * finalizable objects, thus causing cyclic finalizable * objects to be finalized in the correct order. Standard * use involves calling gcPtrNotUsedByFinalizer(&p) * where p is a pointer that is not used by finalization * code, and should not be considered in determining * finalization order. */ GC_EXPORTX(int) gcPtrNotUsedByFinalizer(void **link); /* * If you have called gcDisappearingPtr(link, obj), then *link * will be automatically zeroed when the data pointed to by * obj becomes inaccessible. This will happen before any finalization * occurs. * * Returns 1 if link was already registered, 0 otherwise. * * gcDisappearingPtr is often used when implementing weak pointers * (pointers that are not traced during collection). By ensuring that * the weak pointer is zeroed if the data it is pointing to goes away, * the danger of following a loose weak pointer is eliminated. * * In this case, have link point to a location holding * a disguised pointer to obj. (A pointer inside a "leaf" * object is efficiently disguised.) The pointer is zeroed * when obj becomes inaccessible. Each link may be registered only * once. However, it should be unregistered and reregistered if * the pointer is modified to point at a differenct object. * * Note that obj may be resurrected by another finalizer. */ GC_EXPORTX(int) gcDisappearingPtr(void ** link, void * obj); GC_EXPORTX(int) gcUnregisterDisappearingPtr(void ** link); /* * Converting a hidden pointer to a real pointer requires verifying * that the object still exists. This involves acquiring the * allocator lock to avoid a race with the collector. */ typedef void * (*gcFnType)(void *); GC_EXPORTX(void *) gcCallWithAllocLock(gcFnType fn, void * client_data); /* * If p and q point to the same object returns p else calls gcAbort() * Succeeds if neither p nor q points to the heap. */ GC_EXPORTX(void *) gcSameObj(void *p, void *q); GC_EXPORTX(void) gcSetDirty(void *); GC_EXPORTX(int) gcInSameObj(void *, void *); /* * Safer, but slow, pointer addition. Probably useful mainly with * a preprocessor. Useful only for heap pointers. */ #ifdef GC_DEBUG_BOUNDS_CHECK # define GC_PTR_ADD(x, n) ((gcSameObj((void *)((x)+(n)), (void *)(x))), ((x)+(n))) #else /* !GC_DEBUG_BOUNDS_CHECK */ # define GC_PTR_ADD(x, n) ((x)+(n)) #endif /* Safer assignment of a pointer to a nonstack location. */ #ifdef GC_DEBUG_BOUNDS_CHECK # define GC_PTR_STORE(p, q) \ (*(void **)gcIsVisible(p) = gcIsValidDisplacement(q)) #else /* !GC_DEBUG_BOUNDS_CHECK */ # define GC_PTR_STORE(p, q) *((p) = (q)) #endif /* * gcHidePointer takes a pointer and flips its bits so Great Circle * wont recognise it as a pointer. gcRevealPointer flips them back. */ # define gcHidePointer(p) (~(gcWord)(p)) # define gcRevealPointer(p) ((void *)(gcHidePointer(p))) /* * Under Windows there are operating system calls to get memory * (Global|Local)|(Alloc|ReAlloc). By default, we pass these calls * on to Windows, although we still scan such memory for pointers. * You can redefine this behavior in your gcInitialize() function * by settin gcAllocBehavior to GC_INTERCEPT. In this case, Great * Circle will garbage collect memory allocated by (Global|Local)|(Alloc|ReAlloc) * except when allocated with the (GMEM|LMEM)_(MOVEABLE|DISCARDABLE) flags. */ #define GC_INTERCEPT 0 /* intercept calls */ #define GC_PASS_THROUGH 1 /* DEFAULT: pass through calls trace */ /* their results. */ #define GC_TRACE_ALL 2 /* pass through calls trace their results */ /* on a per-object basis. */ GC_IMPORT int gcAllocBehavior; GC_IMPORT int gcAllocWarn; /* warnings for (Global|Local)|(Alloc|ReAlloc) */ GC_EXPORTX(const char *) gcGetDllName(); GC_IMPORT int gcDontInterceptCRunTimeDLL; /* * Allocates a page, generally 4K, of objects and returns them as a list * linked through their first word. Its use can greatly reduce lock * contention problems in threaded systems, since the allocation lock * can be acquired and released many fewer times. In unthreaded systems * this is pointless. These are always non debug objects. */ GC_EXPORTX(void *) gcMallocMany(size_t lb); /* Retrive the next item in list reutrned by gcMallocMany */ #define GC_NEXT(p) (*(void **)(p)) # define GC_INIT() /* * Call to register root segments. */ GC_EXPORTX(void) gcRegisterDLL(char * static_root); /* these are used to identify the library used */ GC_EXPORTX(const char *) gcLibrary(NO_PARAMS); /* gcall etc */ GC_EXPORTX(const char *) gcCompiler(NO_PARAMS); /* compiler used */ GC_EXPORTX(int) gcVersion(NO_PARAMS); /* version number * 100, 1.1 -> 110 */ GC_EXPORTX(int) gcBuildNo(NO_PARAMS); /* Build number */ GC_EXPORTX(int) gcThreads(NO_PARAMS); /* 1 if thread safe 0 otherwise */ GC_EXPORTX(long) gcEvaluationCopy(); /* Returns non zero for eval copies */ /* * gcFreeX is exactly like free but has a consistent interface. * some systems have a free that cleverly returns an int. */ GC_EXPORTX(void) gcFreeX(void *); enum gcNewType { gcMemDefault = 0, /* don't change threads code uses values */ gcMemAuto = 1, gcMemLeaf = 2, gcMemManual = 3, gcMemAutoIgn = 4, gcMemLeafIgn = 5 }; #ifdef __cplusplus } /* end of extern "C" */ /* C++ Interface to GCTransparent */ # include #define gcSetDirty(x) #define GC_CLASS_HAS_POINTERS \ void * operator new(size_t s) { return gcNewDefaultAuto(s); } \ void * operator new(size_t s, char *f, int l) \ { return gcNewDefaultAuto(s, f, l); } \ void operator delete( void* obj ) { gcFreeX(obj); } #define GC_CLASS_HAS_NO_POINTERS \ void * operator new(size_t s) { return gcNewDefaultLeaf(s); } \ void * operator new(size_t s, char *f, int l) \ { return gcNewDefaultLeaf(s, f, l); } \ void operator delete( void* obj ) { gcFreeX(obj); } #define GC_CLASS_IS_MANUAL \ void * operator new(size_t s) { return gcNewDefaultManual(s); } \ void * operator new(size_t s, char *f, int l) \ { return gcNewDefaultManual(s, f, l); } \ void operator delete( void* obj ) { gcFreeX(obj); } GC_EXPORTX(void *) gcNewDefaultAuto(size_t s); GC_EXPORTX(void *) gcNewDefaultAuto(size_t s, const char *file, int line); GC_EXPORTX(void *) gcNewDefaultLeaf(size_t s); GC_EXPORTX(void *) gcNewDefaultLeaf(size_t s, const char *file, int line); GC_EXPORTX(void *) gcNewDefaultManual(size_t s); GC_EXPORTX(void *) gcNewDefaultManual(size_t s, const char *file, int line); GC_EXPORTY(void *) __cdecl operator new(size_t size); GC_EXPORTY(void *) __cdecl operator new(size_t size, const char *file, int line); GC_EXPORTY(void) __cdecl operator delete(void *); /* * Instances of classes derived from "gc" will be allocated in the * collected heap by default, unless an explicit placement is * specified. */ GC_EXPORTY(class) gc { public: GC_CLASS_HAS_POINTERS void* operator new(size_t, void *p) { return p; } }; extern "C" { GC_EXPORTX(void) gcSetAllocator(gcNewType); } #define GC_NEW(x) (gcSetAllocator(gcMemAuto), new x) #define GC_NEW_LEAF(x) (gcSetAllocator(gcMemLeaf), new x) #define GC_NEW_MANUAL(x) (gcSetAllocator(gcMemManual), new x) #define GC_NEW_IGNORE_OFF_PAGE(x) (gcSetAllocator(gcMemAutoIgn), new x) #define GC_NEW_LEAF_IGNORE_OFF_PAGE(x) (gcSetAllocator(gcMemLeafIgn), new x) #define GC_NEW_ARRAY(s, t) \ (new gcArrayBase(GC_NEW(t[s]), s, sizeof(t))) #define GC_NEW_LEAF_ARRAY(s, t) \ (new gcArrayBase(GC_NEW_LEAF(t[s]), s, sizeof(t))) #define GC_NEW_MANUAL_ARRAY(s, t) \ (new gcArrayBase(GC_NEW_MANUAL(t[s]), s, sizeof(t))) #define GC_NEW_LEAF_IGNORE_OFF_PAGE_ARRAY(s, t) \ (new gcArrayBase(GC_NEW_LEAF_IGNORE_OFF_PAGE(t[s]), s, sizeof(t))) #define GC_NEW_IGNORE_OFF_PAGE_ARRAY(s, t) \ (new gcArrayBase(GC_NEW_IGNORE_OFF_PAGE(t[s]), s, sizeof(t))) /* * Class gcLWCleanup behaves like gcCleanup except that it does not inherit * from class gc. Therefore, you must be sure that your class is being * allocated as garbage collected (e.g. if you are using the gcall library). * gcLWCleanup is useful when private inheritance is making class gc's * operator new inaccessible. */ GC_EXPORTY(class) gcLWCleanup { public: inline gcLWCleanup(); inline virtual ~gcLWCleanup(); private: inline static void cleanup( void* obj ); }; class gcCleanup: virtual public gc, virtual public gcLWCleanup {}; /* * Instances of classes derived from "gcCleanup" will be allocated * in the collected heap by default. When the collector discovers an * inaccessible object derived from "gcCleanup" or containing a * member derived from "gcCleanup", its destructors will be * invoked. */ inline gcLWCleanup::~gcLWCleanup() { gcDeclareFinalizer(this, 0); } inline void gcLWCleanup::cleanup( void* obj ) { ((gcLWCleanup*)obj)->~gcLWCleanup(); } inline gcLWCleanup::gcLWCleanup() { void* base; if (0 != (base = gcBase((void *)this))) gcDeclareFinalizerOffset(base, (gcFinalizationProc)cleanup, (void*)this); } #include #define GC_TEMPLATE template #ifndef GC_DEBUG #define GC_ASSERT(cond, message) #else #define GC_ASSERT(cond, message) if (!(cond)) \ gcErrorPrintf("%s\n", message); #endif /* Parent for wrapped data pointers and references. */ template class gcWrap { public: int gcPtrNotUsedByFinalizer() { return ::gcPtrNotUsedByFinalizer((void **)&data); } protected: gcWrap() { setPointer(); } gcWrap(T *p) { setPointer(p); } gcWrap(const gcWrap< T > &p) { setPointer(p); } /* This exists because stack frames may not be zeroed. */ ~gcWrap() { setPointer(); } void dirtyPointer() { gcSetDirty(&data); } void setPointer() { data = 0; } void setPointer(T* p) { dirtyPointer(); data = p; } void setPointer(const gcWrap< T > &p) { dirtyPointer(); data = p.data; } T* data; /* The pointer to the actual object */ }; template class gcPtr : public gcWrap { public: gcPtr() {} gcPtr(T *p) : gcWrap(p) {} gcPtr(const gcPtr< T > &p) : gcWrap(p) {} T* operator=(T *x) { setPointer(x); return data; } T* operator=(const gcPtr< T > &x) { setPointer(x); return data; } operator T*() const { return data; } operator void*() const { return (void *)data; } T* operator()() const { return data; } T* operator->() const { return data; } int operator!() const { return data == 0; } int operator==(const gcPtr< T > &x) const { return data == x.data; } int operator==(T *x) const { return data == x; } int operator!=(const gcPtr< T > &x) const { return data != x.data; } int operator!=(T *x) const { return data != x; } T& operator[](int n) const; T* operator+(int n) const; T* operator-(int n) const; T* operator+=(int n); T* operator-=(int n); #ifdef GC_DEBUG_BOUNDS_CHECK T* operator=(int n) { GC_ASSERT(!n, "Invalid integer to pointer assignment"); setPointer(); return 0; } T& operator*() const { GC_ASSERT(data, "Dereference of null pointer"); return *data; } #else T* operator=(int) { setPointer(); return 0; } T& operator*() const { return *data; } #endif }; template inline T& gcPtr::operator[]( int n ) const { return *(data + n); } template inline T* gcPtr::operator+(int n) const { return data + n; } template inline T* gcPtr::operator-(int n) const { return data - n; } template inline T* gcPtr::operator+=(int n) { return data = (data + n); } template inline T* gcPtr::operator-=(int n) { return data = (data - n); } template class gcRef : public gcWrap { gcRef() {} gcRef(const T &x) : gcWrap((T *)&x) {} gcRef(const gcRef< T > &x) : gcWrap(x) {} /* The next two operators may not inline right */ void operator=(T &x) { *data = x; } void operator=(const gcRef< T > &x) { *data = x(); x.dirtyPointer(); } T& operator()() const { return *data; } operator T&() const { return *data; } }; /* Return values for gcInBounds tests */ enum gcArrayTest { gcNotAnArray, gcPointerOk, gcPointerTooLow, gcPointerTooHigh, gcPointerAtEnd }; /* Parent of all array class templates. */ class gcArrayBase : public gc { public: gcArrayBase(void *array, size_t count, size_t size) : gcData(array), gcCount(count), gcItemLen(size) {} operator void *() const { return gcData; } size_t size() const { return gcCount * gcItemLen; } void * gcArrayTop() const { return (char *)gcData + size(); } size_t len() const { return gcCount; } void setLen(size_t newLen) { GC_ASSERT((gcCount >= newLen), "Array length set too long"); gcCount = newLen; } int gcValidReference(const void *p) { return p < gcArrayTop() && p >= (void *)*this; } void * operator +(size_t x) { return (char *)(void *)*this + (x * gcItemLen); } gcArrayTest gcInBounds(const void *newP) const { gcArrayTest ii = ( gcArrayTest ) 0; if ((unsigned long)newP < (unsigned long)(void *)*this) ii = gcPointerTooLow; if ((unsigned long)newP > (unsigned long)gcArrayTop()) ii = gcPointerTooHigh; if (!ii) ii = (newP == gcArrayTop()) ? gcPointerAtEnd : gcPointerOk; return ii; } private: void *gcData; size_t gcCount, gcItemLen; }; template class gcArrayPtr { protected: T *data; /* Pointer to array item */ gcArrayBase * array; /* The pointer to the actual array base or zero */ void dirtyPointer() { gcSetDirty(&data); } void setPointer() { data = 0; array = 0; } void setPointer(T* p) { dirtyPointer(); data = p; array = 0; } void setPointer(const gcArrayPtr< T > &p) { dirtyPointer(); data = p.data; array = p.array; } void setPointer(gcArrayBase *p) { dirtyPointer(); data = (T*)(void *)*p; array = p; } public: gcArrayPtr() { setPointer(); } gcArrayPtr(T *p) { setPointer(p); } gcArrayPtr(const gcArrayPtr< T > &p) { setPointer(p); } gcArrayPtr(gcArrayBase *p) { setPointer(p); } int gcPtrNotUsedByFinalizer() { ::gcPtrNotUsedByFinalizer((void **)&array); return ::gcPtrNotUsedByFinalizer((void **)&data); } T* operator=(gcArrayBase *x) { setPointer(x); return data; } T* operator=(T *x) { array = 0; setPointer(x); return data; } T* operator=(const gcArrayPtr< T > &x) { setPointer(x); return data; } operator void*() const { return (void *)data; } T* operator()() const { return data; } int operator!() const { return data == 0; } int operator==(const gcArrayPtr< T > &x) const { return data == x.data; } int operator==(T *x) const { return data == x; } int operator!=(const gcArrayPtr< T > &x) const { return data != x.data; } int operator!=(T *x) const { return data != x; } T* operator+(int n) const { return data + n; } T* operator-(int n) const { return (*this) + -n; } T* operator+=(int n) { return data = (*this + n); } T* operator-=(int n) { return data = (*this + -n); } size_t len() const { return array ? array->len() : 0; } size_t size() const { return array ? array->size() : 0; } void setLen(size_t l) { if (array) array->setLen(l); } #ifdef GC_DEBUG_BOUNDS_CHECK T* operator=(int n) { GC_ASSERT(!n, "Invalid integer to pointer assignment"); setPointer(); return 0; } T& operator[](int n) { GC_ASSERT(array->gcValidReference((void *)(data + n)), "Invalid array reference"); return *(*this + n); } T& operator *() const { GC_ASSERT(array->gcValidReference((void *)data), "Invalid array pointer dereferenced"); return *data; } #else T* operator=(int) { setPointer(); return 0; } T& operator*() const { return *data; } T& operator[](int n) { return *(*this + n); } #endif gcArrayTest gcInObject(void *loc) const { gcArrayTest ii = ( gcArrayTest ) 0; if (!data) ii = gcNotAnArray; else if (array) { if (loc < (void *)*array) ii = gcPointerTooLow; else if (loc > array->gcArrayTop()) ii = gcPointerTooHigh; else if (loc == array->gcArrayTop()) ii = gcPointerAtEnd; else ii = gcPointerOk; } else if (gcInSameObj((void *)data, (void *)loc)) ii = gcPointerOk; else ii = ((void *)loc > (void *)data) ? gcPointerTooHigh : gcPointerTooLow; return (ii); } gcArrayTest gcInBounds(int n) const { return gcInObject(data + n); } }; #endif /* __cplusplus */ # ifdef GC_DEBUG_LINE_NUMBERS # define malloc(sz) gcGlobalMallocDebug((sz), __FILE__, __LINE__) # define calloc(sz, cnt) gcGlobalMallocDebug((sz)*(cnt), __FILE__, __LINE__) # define gcMalloc(sz) gcMallocDebug((sz), __FILE__, __LINE__) # define gcMallocLeaf(sz) gcMallocLeafDebug((sz), __FILE__, __LINE__) # define gcMallocIgnoreOffPage(sz) gcMallocIgnoreOffPageDebug((sz), __FILE__, __LINE__) # define gcMallocLeafIgnoreOffPage(sz) gcMallocLeafIgnoreOffPageDebug((sz), __FILE__, __LINE__) # define gcMallocManual(sz) gcMallocManualDebug((sz), \ __FILE__, __LINE__) # define gcRealloc(old, sz) gcReallocDebug((old), (sz), __FILE__, \ __LINE__) # define realloc(old, sz) gcReallocDebug((old), (sz), __FILE__, \ __LINE__) # define gcFree(p) gcFreeDebug(p) # endif #endif /* _GCT_H */