void GetSharing::MarkAsScanning(PolyObject *obj) { ASSERT(obj->ContainsNormalLengthWord()); PolyWord *lengthWord = ((PolyWord*)obj) - 1; LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord); ASSERT(! space->bitmap.TestBit(space->wordNo(lengthWord))); space->bitmap.SetBit(space->wordNo(lengthWord)); }
// Set the forwarding so that references to objToSet will be forwarded to // objToShare. objToSet will be garbage. void shareWith(PolyObject *objToSet, PolyObject *objToShare) { // We need to remove the bit from this so that we know it's not // a share chain. PolyWord *lengthWord = ((PolyWord*)objToSet) - 1; LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord); ASSERT(space); PLocker locker(&space->bitmapLock); ASSERT(space->bitmap.TestBit(space->wordNo(lengthWord))); space->bitmap.ClearBit(space->wordNo(lengthWord)); // Actually do the forwarding objToSet->SetForwardingPtr(objToShare); }
void GCCopyPhase() { mainThreadPhase = MTP_GCPHASECOMPACT; for(std::vector<LocalMemSpace*>::iterator i = gMem.lSpaces.begin(); i < gMem.lSpaces.end(); i++) { LocalMemSpace *lSpace = *i; uintptr_t highest = lSpace->wordNo(lSpace->top); for (unsigned i = 0; i < NSTARTS; i++) lSpace->start[i] = highest; lSpace->start_index = NSTARTS - 1; lSpace->spaceOwner = 0; // Reset the allocation pointers. This puts garbage (and real data) below them. // At the end of the compaction the allocation pointer will point below the // lowest real data. lSpace->upperAllocPtr = lSpace->top; } // Copy the mutable data into a lower area if possible. if (gpTaskFarm->ThreadCount() == 0) copyAllData(globalTask, 0, 0); else { // We start as many tasks as we have threads. If the amount of work to // be done is very small one thread could process more than one task. // Have to be careful because we use the task ID to decide which space // to scan. for (unsigned j = 0; j < gpTaskFarm->ThreadCount(); j++) gpTaskFarm->AddWorkOrRunNow(©AllData, 0, 0); } gpTaskFarm->WaitForCompletion(); }
bool GetSharing::TestForScan(PolyWord *pt) { PolyObject *obj; // This may be a forwarding pointer left over from a minor GC that did // not complete or it may be a sharing chain pointer that we've set up. while (1) { PolyWord p = *pt; ASSERT(p.IsDataPtr()); obj = p.AsObjPtr(); PolyWord *lengthWord = ((PolyWord*)obj) - 1; LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord); if (space == 0) return false; // Ignore it if it points to a permanent area if (space->bitmap.TestBit(space->wordNo(lengthWord))) return false; // Wasn't marked - must be a forwarding pointer. if (obj->ContainsForwardingPtr()) { obj = obj->GetForwardingPtr(); *pt = obj; } else break; } ASSERT(obj->ContainsNormalLengthWord()); totalVisited += 1; totalSize += obj->Length() + 1; return true; }
objectState getObjectState(PolyObject *p) { PolyWord *lengthWord = ((PolyWord*)p) - 1; LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord); if (space == 0) return REALOBJECT; // May be the address of a permanent or something else. PLocker locker(&space->bitmapLock); if (!p->ContainsForwardingPtr()) return REALOBJECT; if (space->bitmap.TestBit(space->wordNo(lengthWord))) return CHAINED; else return FORWARDED; }
// This deals with weak references within the run-time system. void MTGCCheckWeakRef::ScanRuntimeAddress(PolyObject **pt, RtsStrength weak) { /* If the object has not been marked and this is only a weak reference */ /* then the pointer is set to zero. This allows streams or windows */ /* to be closed if there is no other reference to them. */ PolyObject *val = *pt; PolyWord w = val; if (weak == STRENGTH_STRONG) return; LocalMemSpace *space = gMem.LocalSpaceForAddress(w.AsStackAddr()); if (space == 0) return; // Not in local area // If it hasn't been marked set it to zero. if (! space->bitmap.TestBit(space->wordNo(w.AsStackAddr()))) *pt = 0; }
// Deal with weak objects void MTGCCheckWeakRef::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED L) { if (! OBJ_IS_WEAKREF_OBJECT(L)) return; ASSERT(OBJ_IS_MUTABLE_OBJECT(L)); // Should be a mutable. ASSERT(OBJ_IS_WORD_OBJECT(L)); // Should be a plain object. // See if any of the SOME objects contain unreferenced refs. POLYUNSIGNED length = OBJ_OBJECT_LENGTH(L); PolyWord *baseAddr = (PolyWord*)obj; for (POLYUNSIGNED i = 0; i < length; i++) { PolyWord someAddr = baseAddr[i]; if (someAddr.IsDataPtr()) { LocalMemSpace *someSpace = gMem.LocalSpaceForAddress(someAddr.AsAddress()); if (someSpace != 0) { PolyObject *someObj = someAddr.AsObjPtr(); // If this is a weak object the SOME value may refer to an unreferenced // ref. If so we have to set this entry to NONE. For safety we also // set the contents of the SOME to TAGGED(0). ASSERT(someObj->Length() == 1 && someObj->IsWordObject()); // Should be a SOME node. PolyWord refAddress = someObj->Get(0); LocalMemSpace *space = gMem.LocalSpaceForAddress(refAddress.AsAddress()); if (space != 0) // If the ref is permanent it's always there. { POLYUNSIGNED new_bitno = space->wordNo(refAddress.AsStackAddr()); if (! space->bitmap.TestBit(new_bitno)) { // It wasn't marked so it's otherwise unreferenced. baseAddr[i] = TAGGED(0); // Set it to NONE. someObj->Set(0, TAGGED(0)); // For safety. convertedWeak = true; } } } } } }
// Copy objects from the source space into an earlier space or up within the // current space. static void copyAllData(GCTaskId *id, void * /*arg1*/, void * /*arg2*/) { LocalMemSpace *mutableDest = 0, *immutableDest = 0; for (std::vector<LocalMemSpace*>::reverse_iterator i = gMem.lSpaces.rbegin(); i != gMem.lSpaces.rend(); i++) { LocalMemSpace *src = *i; if (src->spaceOwner == 0) { PLocker lock(©Lock); if (src->spaceOwner == 0) src->spaceOwner = id; else continue; } else if (src->spaceOwner != id) continue; if (debugOptions & DEBUG_GC_ENHANCED) Log("GC: Copy: copying area %p (thread %p) %s \n", src, id, src->spaceTypeString()); // We start at fullGCLowerLimit which is the lowest marked object in the heap // N.B. It's essential that the first set bit at or above this corresponds // to the length word of a real object. uintptr_t bitno = src->wordNo(src->fullGCLowerLimit); // Set the limit to the top so we won't rescan this. That can // only happen if copying takes a very short time and the same // thread runs multiple tasks. src->fullGCLowerLimit = src->top; // src->highest is the bit position that corresponds to the top of // generation we're copying. uintptr_t highest = src->wordNo(src->top); for (;;) { if (bitno >= highest) break; /* SPF version; Invariant: 0 < highest - bitno */ bitno += src->bitmap.CountZeroBits(bitno, highest - bitno); if (bitno >= highest) break; /* first set bit corresponds to the length word */ PolyWord *old = src->wordAddr(bitno); /* Old object address */ PolyObject *obj = (PolyObject*)(old+1); POLYUNSIGNED L = obj->LengthWord(); ASSERT (OBJ_IS_LENGTH(L)); POLYUNSIGNED n = OBJ_OBJECT_LENGTH(L) + 1 ;/* Length of allocation (including length word) */ bitno += n; // Find a mutable space for the mutable objects and an immutable space for // the immutables. We copy objects into earlier spaces or within its own // space but we don't copy an object to a later space. This avoids the // risk of copying an object multiple times. Previously this copied objects // into later spaces but that doesn't work well if we have converted old // saved state segments into local areas. It's much better to delete them // if possible. bool isMutable = OBJ_IS_MUTABLE_OBJECT(L); LocalMemSpace *destSpace = isMutable || immutableDest == 0 ? mutableDest : immutableDest; PolyWord *newp = FindFreeAndAllocate(destSpace, (src == destSpace) ? bitno : 0, n); if (newp == 0 && src != destSpace) { // See if we can find a different space. // N.B. FindNextSpace side-effects mutableDest/immutableDest to give the next space. if (FindNextSpace(src, isMutable ? &mutableDest : &immutableDest, isMutable, id)) { bitno -= n; // Redo this object continue; } // else just leave it } if (newp == 0) /* no room */ { // We're not going to move this object // Update src->upperAllocPtr, so the old object doesn't get trampled. if (old < src->upperAllocPtr) src->upperAllocPtr = old; // Previously this continued compressing to try to make space available // on the next GC. Normally full GCs are infrequent so the chances are // that at the next GC other data will have been freed. Just stop at // this point. // However if we're compressing a mutable area and there is immutable // data in it we should move those out because the mutable area is scanned // on every partial GC. if (! src->isMutable || src->i_marked == 0) break; } else { PolyObject *destAddress = (PolyObject*)(newp+1); obj->SetForwardingPtr(destAddress); CopyObjectToNewAddress(obj, destAddress, L); if (debugOptions & DEBUG_GC_DETAIL) Log("GC: Copy: %p %lu %u -> %p\n", obj, OBJ_OBJECT_LENGTH(L), GetTypeBits(L), destAddress); } } if (mutableDest == src) mutableDest = 0; if (immutableDest == src) immutableDest = 0; } }