#define TI_NO_SRCPOS /* Code to support distributed garbage collection. Symlink this file into every backend that needs it. */ /* Right now there is no true DGC...we have local GCs that conservatively assume that pointers that escape outside a node are always valid. This is done just by keeping a hash table with the addresses, which the local GCs will see when it does its sweep. */ #include "backend.h" #include #include "gp-type.h" #include "ti-gc.h" #if defined(USE_DISTRIBUTED_GC) && !defined(USE_GC_NONE) #define DGC_HASH_SIZE 8209 /* Some prime number. Whatever. */ /* DOB: all accesses to the following data structures are protected using * AM_lock to ensure correct operation on SMP's - we use AM_lock because * that's the only lock safe to acquire from within an AM-handler * (some of which call these entry points) * * GASNet uses a separate handler-safe lock instead */ #ifdef COMM_GASNET static gasnet_hsl_t dgc_hsl = GASNET_HSL_INITIALIZER; #define TI_DGC_LOCK(AM_handler_context) gasnet_hsl_lock(&dgc_hsl) #define TI_DGC_UNLOCK(AM_handler_context) gasnet_hsl_unlock(&dgc_hsl) #else #define TI_DGC_LOCK(AM_handler_context) \ if (!AM_handler_context) TIC_AM_LOCK() #define TI_DGC_UNLOCK(AM_handler_context) \ if (!AM_handler_context) TIC_AM_UNLOCK() #endif static int dgc_hash_size; static void **dgc_hash; void dgc_init(void) { int i; dgc_hash_size = DGC_HASH_SIZE; dgc_hash = (void *)ti_malloc_huge(dgc_hash_size * sizeof(void *)); /* automatically gets cleared to null */ } /* return the number of pointers currently hashed (for statistical purposes) */ int dgc_numhashed(void) { int count = 0; int i; TI_DGC_LOCK(0); for (i = 0; i < dgc_hash_size; i++) { if (dgc_hash[i]) count++; } TI_DGC_UNLOCK(0); return count; } TI_INLINE(dgc_insert) void dgc_insert(void *ptr) { unsigned int const hashkey = ((unsigned int)(unsigned long)ptr) % dgc_hash_size; int j = hashkey; /* Open addressing. */ while (dgc_hash[j] != NULL && dgc_hash[j] != ptr) { j++; if (j >= dgc_hash_size) { j = 0; } if (j == hashkey) { /* table full - grow & rehash the table */ void **old_dgc_hash = dgc_hash; int old_dgc_hash_size = dgc_hash_size; int i; dgc_hash_size *= 2; dgc_hash = (void *)ti_malloc_huge(dgc_hash_size * sizeof(void *)); for (i=0; i < old_dgc_hash_size; i++) { /* rehash */ dgc_insert(old_dgc_hash[i]); } dgc_insert(ptr); /* add the one that failed */ /* clear & free the old table */ memset(old_dgc_hash, 0, old_dgc_hash_size * sizeof(void *)); ti_free(old_dgc_hash); return; } } if (dgc_hash[j] == NULL) dgc_hash[j] = ptr; } /* isHeapPointer: query the garbage collector and return whether or not * p points to a valid heap location */ #if 1 && !defined(USE_GC_NONE) /* DOB: another, possibly faster possibility is GC_find_header(p) != 0 */ #define isHeapPointer(p) (GC_base(p)) #else #define isHeapPointer(p) (1) #endif /* *** See ti-gc.h for description of these entry points *** */ /* Treats addrblock as a pointer to a contiguous block of global pointers, all of which must be hashed into the hash table. */ void dgc_ptr_esc(void *addrblock, int num_gps, int AM_handler_context) { if (ti_never_gc) return; TI_DGC_LOCK(AM_handler_context); { jGPointer *gptr_arr = (jGPointer *)addrblock; unsigned int i; /* hash any gp's to our memory space */ for (i = 0; i < num_gps; i++) { if (isDirectlyAddressable(gptr_arr[i])) { void *lp = TO_LOCAL(gptr_arr[i]); if (lp && isHeapPointer(lp)) dgc_insert(lp); } } } TI_DGC_UNLOCK(AM_handler_context); return; /* should be the ONLY return in this fn */ } /* Used when we don't know what the global pointers are---hash everything. */ void dgc_ptr_esc_all(void *addrblock, int num_bytes, int AM_handler_context) { if (ti_never_gc) return; TI_DGC_LOCK(AM_handler_context); { void **lastptr = (void **)( ((jbyte*)addrblock) + num_bytes - sizeof(void *) ); void **ptr; /* Treat everything like a local pointer and hash it */ for (ptr = (void **)addrblock; ptr <= lastptr; ptr++) { void *lp = *ptr; if (lp && isHeapPointer(lp)) dgc_insert(lp); } } TI_DGC_UNLOCK(AM_handler_context); return; /* should be the ONLY return in this fn */ } /* Used when we don't know what the global pointers are, * but are guaranteed that they take the form of global pointers and NOT local pointers */ void dgc_ptr_esc_all_gps(void *addrblock, int num_bytes, int AM_handler_context) { if (ti_never_gc) return; TI_DGC_LOCK(AM_handler_context); { void **lastptr = (void **)( ((jbyte*)addrblock) + num_bytes - sizeof(jGPointer) ); void **ptr; /* Treat everything like a local pointer and hash it */ for (ptr = (void **)addrblock; ptr <= lastptr; ptr++) { jGPointer gp = *(jGPointer*)ptr; int box = TO_BOX(gp); #if 0 int proc = TO_PROC(gp); /* check both fields of the gp for more precise recognition */ if (box == MYBOX && proc >= 0 && proc < ___PROCS && COMM_GetBoxNumberForProcNumber(proc) == MYBOX) { #else /* no longer have redundant fields in gps */ if (box == MYBOX) { #endif void *lp = TO_LOCAL(gp); if (lp && isHeapPointer(lp)) dgc_insert(lp); } } } TI_DGC_UNLOCK(AM_handler_context); return; /* should be the ONLY return in this fn */ } #if 0 /* DOB: these work, but are not currently being used for anything, * mostly because they have subtle synchronization problems * (in order to ensure correctness, you must mark the pointers * while moving them - if you do it before or after, you risk * something being changed in the interval) */ #ifdef COMM_AM2 /* The AM request handlers to access these entry points remotely */ void ___distgc_mark_escape_handler(void *token, int addr, int num_gps) { TI_DGC_LOCK(1); dgc_ptr_esc((void *)addr, num_gps, 1); TI_DGC_UNLOCK(1); TI_AM_Reply0(token, __NULL_RH); } void ___distgc_mark_escape_all_handler(void *token, int addr, int num_bytes) { TI_DGC_LOCK(1); dgc_ptr_esc_all((void *)addr, num_bytes, 1); TI_DGC_UNLOCK(1); TI_AM_Reply0(token, __NULL_RH); } void ___distgc_mark_escape_all_gps_handler(void *token, int addr, int num_bytes) { TI_DGC_LOCK(1); dgc_ptr_esc_all_gps((void *)addr, num_bytes, 1); TI_DGC_UNLOCK(1); TI_AM_Reply0(token, __NULL_RH); } #endif #endif #endif /* #if defined(USE_DISTRIBUTED_GC) && !defined(USE_GC_NONE) */