// Mark all the roots. This is run in the main thread and has the effect // of starting new tasks as the scanning runs. void MTGCProcessMarkPointers::MarkRoots(void) { ASSERT(nThreads >= 1); ASSERT(nInUse == 0); MTGCProcessMarkPointers *marker = &markStacks[0]; marker->Reset(); marker->active = true; nInUse = 1; // Scan the permanent mutable areas. for (unsigned j = 0; j < gMem.npSpaces; j++) { PermanentMemSpace *space = gMem.pSpaces[j]; if (space->isMutable && ! space->byteOnly) marker->ScanAddressesInRegion(space->bottom, space->top); } // Scan the RTS roots. GCModules(marker); ASSERT(marker->markStack[0] == 0); // When this has finished there may well be other tasks running. PLocker lock(&stackLock); marker->active = false; nInUse--; }
void GCSharingPhase(void) { mainThreadPhase = MTP_GCPHASESHARING; GetSharing sharer; for (std::vector<LocalMemSpace*>::iterator i = gMem.lSpaces.begin(); i < gMem.lSpaces.end(); i++) { LocalMemSpace *lSpace = *i; lSpace->bitmap.ClearBits(0, lSpace->spaceSize()); } // Scan the code areas to share any constants. We don't share the code // cells themselves. for (std::vector<CodeSpace *>::iterator i = gMem.cSpaces.begin(); i < gMem.cSpaces.end(); i++) { CodeSpace *space = *i; sharer.ScanAddressesInRegion(space->bottom, space->top); } if (debugOptions & DEBUG_GC) Log("GC: Share: After scanning code: Total %" POLYUFMT " (%" POLYUFMT " words) byte %" POLYUFMT " word %" POLYUFMT ".\n", sharer.totalVisited, sharer.totalSize, sharer.byteAdded, sharer.wordAdded); // Process the permanent mutable areas and the code areas for (std::vector<PermanentMemSpace*>::iterator i = gMem.pSpaces.begin(); i < gMem.pSpaces.end(); i++) { PermanentMemSpace *space = *i; if (space->isMutable && ! space->byteOnly) sharer.ScanAddressesInRegion(space->bottom, space->top); } if (debugOptions & DEBUG_GC) Log("GC: Share: After scanning permanent: Total %" POLYUFMT " (%" POLYUFMT " words) byte %" POLYUFMT " word %" POLYUFMT ".\n", sharer.totalVisited, sharer.totalSize, sharer.byteAdded, sharer.wordAdded); // Process the RTS roots. GCModules(&sharer); if (debugOptions & DEBUG_GC) Log("GC: Share: After scanning other roots: Total %" POLYUFMT " (%" POLYUFMT " words) byte %" POLYUFMT " word %" POLYUFMT ".\n", sharer.totalVisited, sharer.totalSize, sharer.byteAdded, sharer.wordAdded); gHeapSizeParameters.RecordGCTime(HeapSizeParameters::GCTimeIntermediate, "Table"); // Sort and merge the data. sharer.SortData(); gHeapSizeParameters.RecordGCTime(HeapSizeParameters::GCTimeIntermediate, "Sort"); }
// Called by the root thread to actually save the state and write the file. void SaveRequest::Perform() { // Check that we aren't overwriting our own parent. for (unsigned q = 0; q < newHierarchy-1; q++) { if (sameFile(hierarchyTable[q]->fileName, fileName)) { errorMessage = "File being saved is used as a parent of this file"; errCode = 0; return; } } SaveStateExport exports; // Open the file. This could quite reasonably fail if the path is wrong. exports.exportFile = _tfopen(fileName, _T("wb")); if (exports.exportFile == NULL) { errorMessage = "Cannot open save file"; errCode = errno; return; } // Scan over the permanent mutable area copying all reachable data that is // not in a lower hierarchy into new permanent segments. CopyScan copyScan(newHierarchy); copyScan.initialise(false); bool success = true; try { for (unsigned i = 0; i < gMem.npSpaces; i++) { PermanentMemSpace *space = gMem.pSpaces[i]; if (space->isMutable && ! space->noOverwrite && ! space->byteOnly) copyScan.ScanAddressesInRegion(space->bottom, space->top); } } catch (MemoryException &) { success = false; } // Copy the areas into the export object. Make sufficient space for // the largest possible number of entries. exports.memTable = new memoryTableEntry[gMem.neSpaces+gMem.npSpaces+1]; exports.ioMemEntry = 0; // The IO vector. unsigned memTableCount = 0; MemSpace *ioSpace = gMem.IoSpace(); exports.memTable[0].mtAddr = ioSpace->bottom; exports.memTable[0].mtLength = (char*)ioSpace->top - (char*)ioSpace->bottom; exports.memTable[0].mtFlags = 0; exports.memTable[0].mtIndex = 0; memTableCount++; // Permanent spaces at higher level. These have to have entries although // only the mutable entries will be written. for (unsigned w = 0; w < gMem.npSpaces; w++) { PermanentMemSpace *space = gMem.pSpaces[w]; if (space->hierarchy < newHierarchy) { memoryTableEntry *entry = &exports.memTable[memTableCount++]; entry->mtAddr = space->bottom; entry->mtLength = (space->topPointer-space->bottom)*sizeof(PolyWord); entry->mtIndex = space->index; if (space->isMutable) { entry->mtFlags = MTF_WRITEABLE; if (space->noOverwrite) entry->mtFlags |= MTF_NO_OVERWRITE; if (space->byteOnly) entry->mtFlags |= MTF_BYTES; } else entry->mtFlags = MTF_EXECUTABLE; } } unsigned permanentEntries = memTableCount; // Remember where new entries start. // Newly created spaces. for (unsigned i = 0; i < gMem.neSpaces; i++) { memoryTableEntry *entry = &exports.memTable[memTableCount++]; PermanentMemSpace *space = gMem.eSpaces[i]; entry->mtAddr = space->bottom; entry->mtLength = (space->topPointer-space->bottom)*sizeof(PolyWord); entry->mtIndex = space->index; if (space->isMutable) { entry->mtFlags = MTF_WRITEABLE; if (space->noOverwrite) entry->mtFlags |= MTF_NO_OVERWRITE; if (space->byteOnly) entry->mtFlags |= MTF_BYTES; } else entry->mtFlags = MTF_EXECUTABLE; } exports.memTableEntries = memTableCount; exports.ioSpacing = IO_SPACING; // Update references to moved objects. SaveFixupAddress fixup; for (unsigned l = 0; l < gMem.nlSpaces; l++) { LocalMemSpace *space = gMem.lSpaces[l]; fixup.ScanAddressesInRegion(space->bottom, space->lowerAllocPtr); fixup.ScanAddressesInRegion(space->upperAllocPtr, space->top); } GCModules(&fixup); // Update the global memory space table. Old segments at the same level // or lower are removed. The new segments become permanent. // Try to promote the spaces even if we've had a failure because export // spaces are deleted in ~CopyScan and we may have already copied // some objects there. if (! gMem.PromoteExportSpaces(newHierarchy) || ! success) { errorMessage = "Out of Memory"; errCode = ENOMEM; return; } // Remove any deeper entries from the hierarchy table. while (hierarchyDepth > newHierarchy-1) { hierarchyDepth--; delete(hierarchyTable[hierarchyDepth]); hierarchyTable[hierarchyDepth] = 0; } // Write out the file header. SavedStateHeader saveHeader; memset(&saveHeader, 0, sizeof(saveHeader)); saveHeader.headerLength = sizeof(saveHeader); strncpy(saveHeader.headerSignature, SAVEDSTATESIGNATURE, sizeof(saveHeader.headerSignature)); saveHeader.headerVersion = SAVEDSTATEVERSION; saveHeader.segmentDescrLength = sizeof(SavedStateSegmentDescr); if (newHierarchy == 1) saveHeader.parentTimeStamp = exportTimeStamp; else { saveHeader.parentTimeStamp = hierarchyTable[newHierarchy-2]->timeStamp; saveHeader.parentNameEntry = sizeof(TCHAR); // Always the first entry. } saveHeader.timeStamp = time(NULL); saveHeader.segmentDescrCount = exports.memTableEntries; // One segment for each space. // Write out the header. fwrite(&saveHeader, sizeof(saveHeader), 1, exports.exportFile); // We need a segment header for each permanent area whether it is // actually in this file or not. SavedStateSegmentDescr *descrs = new SavedStateSegmentDescr [exports.memTableEntries]; for (unsigned j = 0; j < exports.memTableEntries; j++) { memoryTableEntry *entry = &exports.memTable[j]; memset(&descrs[j], 0, sizeof(SavedStateSegmentDescr)); descrs[j].relocationSize = sizeof(RelocationEntry); descrs[j].segmentIndex = (unsigned)entry->mtIndex; descrs[j].segmentSize = entry->mtLength; // Set this even if we don't write it. descrs[j].originalAddress = entry->mtAddr; if (entry->mtFlags & MTF_WRITEABLE) { descrs[j].segmentFlags |= SSF_WRITABLE; if (entry->mtFlags & MTF_NO_OVERWRITE) descrs[j].segmentFlags |= SSF_NOOVERWRITE; if (j < permanentEntries && (entry->mtFlags & MTF_NO_OVERWRITE) == 0) descrs[j].segmentFlags |= SSF_OVERWRITE; if (entry->mtFlags & MTF_BYTES) descrs[j].segmentFlags |= SSF_BYTES; } } // Write out temporarily. Will be overwritten at the end. saveHeader.segmentDescr = ftell(exports.exportFile); fwrite(descrs, sizeof(SavedStateSegmentDescr), exports.memTableEntries, exports.exportFile); // Write out the relocations and the data. for (unsigned k = 1 /* Not IO area */; k < exports.memTableEntries; k++) { memoryTableEntry *entry = &exports.memTable[k]; // Write out the contents if this is new or if it is a normal, overwritable // mutable area. if (k >= permanentEntries || (entry->mtFlags & (MTF_WRITEABLE|MTF_NO_OVERWRITE)) == MTF_WRITEABLE) { descrs[k].relocations = ftell(exports.exportFile); // Have to write this out. exports.relocationCount = 0; // Create the relocation table. char *start = (char*)entry->mtAddr; char *end = start + entry->mtLength; for (PolyWord *p = (PolyWord*)start; p < (PolyWord*)end; ) { p++; PolyObject *obj = (PolyObject*)p; POLYUNSIGNED length = obj->Length(); // Most relocations can be computed when the saved state is // loaded so we only write out the difficult ones: those that // occur within compiled code. // exports.relocateObject(obj); if (length != 0 && obj->IsCodeObject()) machineDependent->ScanConstantsWithinCode(obj, &exports); p += length; } descrs[k].relocationCount = exports.relocationCount; // Write out the data. descrs[k].segmentData = ftell(exports.exportFile); fwrite(entry->mtAddr, entry->mtLength, 1, exports.exportFile); } } // If this is a child we need to write a string table containing the parent name. if (newHierarchy > 1) { saveHeader.stringTable = ftell(exports.exportFile); _fputtc(0, exports.exportFile); // First byte of string table is zero _fputts(hierarchyTable[newHierarchy-2]->fileName, exports.exportFile); _fputtc(0, exports.exportFile); // A terminating null. saveHeader.stringTableSize = (_tcslen(hierarchyTable[newHierarchy-2]->fileName) + 2)*sizeof(TCHAR); } // Rewrite the header and the segment tables now they're complete. fseek(exports.exportFile, 0, SEEK_SET); fwrite(&saveHeader, sizeof(saveHeader), 1, exports.exportFile); fwrite(descrs, sizeof(SavedStateSegmentDescr), exports.memTableEntries, exports.exportFile); // Add an entry to the hierarchy table for this file. (void)AddHierarchyEntry(fileName, saveHeader.timeStamp); delete[](descrs); }
void GCheckWeakRefs() { MTGCCheckWeakRef checkRef; GCModules(&checkRef); checkRef.ScanAreas(); }