// fill in the data "behind" bags struct OpaqueBag { void * body;
};
GAP_STATIC_ASSERT(sizeof(void *) == sizeof(struct OpaqueBag), "sizeof(OpaqueBag) is wrong");
// BAG_SLACK is used to define a block of empty space at the end of each // bag, which can then be marked as "not accessible" in the memory checker // Valgrind
enum { BAG_SLACK = 0 };
// TIGHT_WORDS_BAG defines the actual amount of space a Bag requires, // without BAG_SLACK. staticinline UInt TIGHT_WORDS_BAG(UInt size)
{ return (size + sizeof(Bag) - 1) / sizeof(Bag);
}
/* These macros, are (a) for more readable code, but more importantly (b)toensurethatunsignedsubtractsanddividesareused(since weknowtheorderingofthepointers.Thisisneededon>2GB workspaceson32butsystems.TheSize****Areafunctionsreturnan answerinunitsofaword(iesizeof(UInt)bytes),whichshould
therefore be small enough not to cause problems. */
staticinline UInt SpaceBetweenPointers(constvoid * a, constvoid * b)
{
GAP_ASSERT(b <= a);
UInt res = (((UInt)((UInt)(a) - (UInt)(b))) / sizeof(Bag)); return res;
}
// tell valgrind that the masterpointer, bag contents and bag header of Bag // should all be accessible staticvoid CANARY_ALLOW_ACCESS_BAG(Bag bag)
{
VALGRIND_MAKE_MEM_DEFINED(bag, sizeof(Bag)); char * ptr = (char *)PTR_BAG(bag); Int bagLength = SIZE_BAG(bag);
VALGRIND_MAKE_MEM_DEFINED(ptr, bagLength);
// Reverse CANARY_ALL_ACCESS_BAG, making the masterpointer, bag contents and // bag header all inaccessible staticvoid CANARY_FORBID_ACCESS_BAG(Bag bag)
{
VALGRIND_MAKE_MEM_NOACCESS(bag, sizeof(Bag)); char * ptr = (char *)PTR_BAG(bag); Int bagLength = SIZE_BAG(bag);
VALGRIND_MAKE_MEM_NOACCESS(ptr, bagLength);
// Mark all bags as accessible staticvoid CANARY_ALLOW_ACCESS_ALL_BAGS(void)
{
CallbackForAllBags(CANARY_ALLOW_ACCESS_BAG);
}
// Mark all bags as inaccessible staticvoid CANARY_FORBID_ACCESS_ALL_BAGS(void)
{
VALGRIND_MAKE_MEM_NOACCESS(MptrBags, SizeWorkspace * sizeof(Bag));
}
// Temporarily disable valgrind checking. This is used while creating bags or // adjusting any internal GASMAN structures #define CANARY_DISABLE_VALGRIND() VALGRIND_DISABLE_ERROR_REPORTING
// We define MarkBag as a inline function here so that // the compiler can optimize the marking functions using it in the // "current translation unit", i.e. inside gasman.c. // Other marking functions don't get to inline MarkBag calls anymore, // but luckily these are rare (and usually not performance critical // to start with). inlinevoid MarkBag(Bag bag, void * ref)
{ if ( IS_BAG_ID(bag)
&& YoungBags < CONST_PTR_BAG(bag) // points to a young bag
&& CONST_PTR_BAG(bag) <= AllocBags // " " " " "
&& (IS_MARKED_DEAD(bag) || IS_MARKED_HALFDEAD(bag)) )
{
LINK_BAG(bag) = MarkedBags;
MarkedBags = bag;
} #ifdef DEBUG_GASMAN_MARKING elseif (!DisableMarkBagValidation) { if (bag != 0 && !((UInt)bag & 3) && !IS_BAG_ID(bag)) {
BadMarksCounter++;
}
} #endif
}
void MarkBagWeakly(Bag bag)
{ if ( IS_BAG_ID(bag)
&& YoungBags < CONST_PTR_BAG(bag) // points to a young bag
&& CONST_PTR_BAG(bag) <= AllocBags // " " " " "
&& IS_MARKED_DEAD(bag) ) // and not marked already
{ // mark it now as we don't have to recurse
LINK_BAG(bag) = MARKED_HALFDEAD(bag);
}
}
if ( GlobalBags.nr == NR_GLOBAL_BAGS ) {
Panic("Gasman cannot handle so many global variables");
}
if (cookie == 0) {
Panic("Gasman got a NULL cookie");
}
for (UInt i = 0; i < GlobalBags.nr; i++) { if (streq(GlobalBags.cookie[i], cookie)) { if (GlobalBags.addr[i] == addr)
Pr("Duplicate global bag entry %s\n", (Int)cookie, 0); else
Pr("Duplicate global bag cookie %s\n", (Int)cookie, 0);
}
}
int enableMemCheck(constchar * argv[], void * dummy)
{
fputs("# Warning: --enableMemCheck causes SEVERE slowdowns. Starting GAP may take several days!\n", stderr);
EnableMemCheck = 1; return1;
}
staticvoid MoveBagMemory(char * oldbase, char * newbase)
{ Int moveSize = (newbase - oldbase) / sizeof(Bag); // update the masterpointers for (Bag bag = MptrBags; bag < MptrEndBags; bag++) { if (MptrEndBags <= (Bag)bag->body)
bag->body = (Bag)bag->body + moveSize;
}
Int newBase = oldBase + 1; // Memory buffer 0 is special, as we use that // copy for the master pointers. Therefore never // block access to it, and skip it when cycling. if (newBase >= GetMembufCount())
newBase = 1;
// call the before functions (if any)
UInt i; for (i = 0; i < CollectFuncBags.nrBefore; ++i)
CollectFuncBags.before[i]();
// Enable access to new memory
mprotect(GetMembuf(newBase), GetMembufSize(), PROT_READ | PROT_WRITE); // Block access to old memory (except block 0, which will only occur // on the first call). if (oldBase != 0) {
mprotect(GetMembuf(oldBase), GetMembufSize(), PROT_NONE);
}
// call the after functions (if any) for (i = 0; i < CollectFuncBags.nrAfter; ++i)
CollectFuncBags.after[i]();
oldBase = newBase;
}
#endif
/**************************************************************************** ** *FReportOutOfMemory_ ** **The'1<<27'checkensuresweonlytrytorecoverwhenalarge **allocationfails.
*/ staticvoid ReportOutOfMemory_(int line, UInt size, constchar * reason)
{ // if a small allocation failed, we are in serious trouble if (size <= (1 << 27))
Panic_(__FILE__, line, "%s", reason);
// If a SyStorOverrun message is scheduled, mark it as already printed if (SyStorOverrun == SY_STOR_OVERRUN_TO_REPORT)
SyStorOverrun = SY_STOR_OVERRUN_REPORTED;
ErrorMayQuit("Cannot allocate %d bytes: %s", size, (Int)reason);
}
void InitBags(UInt initial_size, Bag * stack_bottom)
{ // fix max if it is lower than min if (SyStorMax != 0 && SyStorMax < SyStorMin) {
SyStorMax = SyStorMin;
}
// fix pool size if larger than SyStorKill if (SyStorKill != 0 && SyAllocPool != 0 &&
SyAllocPool > 1024 * SyStorKill) {
SyAllocPool = SyStorKill * 1024;
}
ClearGlobalBags();
// install the allocator and the abort function
ExtraMarkFuncBags = 0;
// install the stack values
StackBottomBags = stack_bottom;
// first get some storage from the operating system
initial_size = (initial_size + 511) & ~(511);
MptrBags = SyAllocBags( initial_size, 1 );
GAP_ASSERT(MptrBags);
EndBags = (Bag *)MptrBags + 1024 * (initial_size / sizeof(Bag *));
// In GAP_MEM_CHECK we want as few master pointers as possible, as we // have to loop over them very frequently. #ifdef GAP_MEM_CHECK
UInt initialBagCount = 100000; #else
UInt initialBagCount = 1024*initial_size/8/sizeof(Bag*); #endif // 1/8th of the storage goes into the masterpointer area
FreeMptrBags = MptrBags; for (Bag bag = MptrBags; bag + 1 < MptrBags + initialBagCount; bag++) {
bag->body = bag + 1;
}
// the rest is for bags
MptrEndBags = MptrBags + initialBagCount; // Add a small gap between the end of the master pointers and OldBags // This is mainly here to ensure we do not break allowing OldBags and // MptrEndBags to differ.
OldBags = (Bag *)MptrEndBags + 10;
YoungBags = OldBags;
AllocBags = OldBags;
AllocSizeBags = 256;
// install the marking functions for (UInt i = 0; i < NUM_TYPES; i++)
TabMarkFuncBags[i] = MarkAllSubBagsDefault;
// Set ChangedBags to a proper initial value
ChangedBags = 0;
/**************************************************************************** ** *FNewBag(<type>,<size>)..............allocateanewbag ** **'NewBag'isactuallyquitesimple. ** **Itfirsttestswhetherenoughstorageisavailableintheallocationarea **andwhetherafreemasterpointerisavailable.Ifnot,itstartsa **garbagecollectionbycalling'CollectBags'passing<size>asthesizeof **thebagitiscurrentlyallocatingand0toindicatethatonlyapartial **garbagecollectioniscalledfor.If'CollectBags'failsandreturns0, **'NewBag'alsofailsandalsoreturns0. ** **Thenittakesthefirstfreemasterpointerfromthelinkedlistoffree **masterpointers(see"FreeMptrBags"). ** **Thenitwritesthesizeandthetypeintothewordpointedtoby **'AllocBags'.Thenitwritestheidentifier,i.e.,thelocationofthe **masterpointer,intothenextword. ** **Thenitadvances'AllocBags'by'2+WORDS_BAG(<size>)'. ** **Finallyitreturnstheidentifierofthenewbag. ** **Allentriesofthenewbagwillbeinitializedto0. ** **If{\Gasman}wascompiledwiththeoption'COUNT_BAGS'then'NewBag'also **updatestheinformationin'InfoBags'(see"InfoBags"). ** **'NewBag'isimplementedasafunctioninsteadofamacroforthree **reasons.Itreducesthesizeoftheprogram,improvingtheinstruction **cachehitratio.Thecompilercandoanti-aliasinganalysisforthe **localvariablesofthefunction.Toenablestatisticsonly{\Gasman} **needstoberecompiled.
*/
Bag NewBag (
UInt type,
UInt size )
{
Bag bag; // identifier of the new bag
#ifdef GAP_MEM_CHECK
MaybeMoveBags(); #endif
#ifdef TREMBLE_HEAP
CollectBags(0,0); #endif
GAP_ASSERT(!InWorkspaceRestore());
CANARY_DISABLE_VALGRIND();
// check that a masterpointer and enough storage are available if ( (FreeMptrBags == 0 || SizeAllocationArea < WORDS_BAG(sizeof(BagHeader)+size))
&& CollectBags( size, 0 ) == 0 )
{
ReportOutOfMemory(size, "cannot extend the workspace any more!!");
}
// if the real size of the bag doesn't change, not much needs to be done if ( diff == 0 ) {
header->size = new_size;
}
// if the bag is shrunk we insert a magic marker into the heap // Note: if the bag is the last bag, we could in theory also shrink it // by moving 'AllocBags', however this is not correct as the "freed" // memory may not be zero filled, and zeroing it out would cost us elseif ( diff < 0 ) {
// leave magic size-type word for the sweeper, type must be T_DUMMY
BagHeader * freeHeader = (BagHeader *)(DATA(header) + WORDS_BAG(new_size));
freeHeader->type = T_DUMMY; if ( diff == -1 ) { // if there is only one free word, avoid setting the size in // the header: there is no space for it on 32bit systems; // instead set flags to 1 to inform the sweeper.
freeHeader->flags = 1;
} else {
freeHeader->flags = 0;
freeHeader->size = (-diff-1)*sizeof(Bag);
}
header->size = new_size;
}
// if the last bag is enlarged ... elseif (CONST_PTR_BAG(bag) + WORDS_BAG(old_size) == AllocBags) { // check that enough storage for the new bag is available if (SpaceBetweenPointers(EndBags, CONST_PTR_BAG(bag)) < WORDS_BAG(new_size)
&& CollectBags( new_size-old_size, 0 ) == 0 ) {
ReportOutOfMemory(new_size, "cannot extend the workspace any more!!!");
}
// update header pointer in case bag moved
header = BAG_HEADER(bag);
// simply increase the free pointer if ( YoungBags == AllocBags )
YoungBags += diff;
AllocBags += diff;
// and increase the total amount allocated by the difference #ifdef COUNT_BAGS
InfoBags[type].sizeAll += new_size - old_size; #endif
SizeAllBags += new_size - old_size;
header->size = new_size;
}
// if the bag is enlarged ... else {
// check that enough storage for the new bag is available if ( SizeAllocationArea < WORDS_BAG(sizeof(BagHeader)+new_size)
&& CollectBags( new_size, 0 ) == 0 ) {
ReportOutOfMemory(new_size, "cannot extend the workspace any more!!!!");
}
// update header pointer in case bag moved
header = BAG_HEADER(bag);
// leave magic size-type word for the sweeper, type must be T_DUMMY
header->type = T_DUMMY;
header->flags = 0;
header->size = sizeof(BagHeader) + (TIGHT_WORDS_BAG(old_size) - 1) * sizeof(Bag);
// allocate the storage for the bag
BagHeader * newHeader = (BagHeader *)AllocBags;
AllocBags = DATA(newHeader) + WORDS_BAG(new_size);
CANARY_DISABLE_VALGRIND(); // if the bag is already on the changed bags list, keep it there if ( header->link != bag ) {
newHeader->link = header->link;
}
// if the bag is old, put it onto the changed bags list elseif (CONST_PTR_BAG(bag) <= YoungBags) {
newHeader->link = ChangedBags;
ChangedBags = bag;
}
// if the bag is young, enter the normal link word else {
newHeader->link = bag;
}
CANARY_ENABLE_VALGRIND();
// set the masterpointer
Bag * dst = DATA(newHeader);
SET_PTR_BAG(bag, dst);
// copy the contents of the bag
SyMemmove((void *)dst, (void *)DATA(header), sizeof(Obj) * WORDS_BAG(old_size));
}
if (pA < pB) { for (i = 0; i < sizeof(Bag *); i += C_STACK_ALIGN) { for (p = (Bag *)((char *)pA + i); p < pB; p++) {
Bag * pcpy = p; #ifdefined(GAP_MEMORY_CANARY) // Need to mark this pointer as readable for valgrind
VALGRIND_MAKE_MEM_DEFINED(&pcpy, sizeof(pcpy)); #endif
MarkBag(*pcpy, 0);
}
}
} else { for (i = 0; i < sizeof(Bag *); i += C_STACK_ALIGN) { for (p = (Bag *)((char *)pA - i); pB < p; p--) {
Bag * pcpy = p; #ifdefined(GAP_MEMORY_CANARY) // Need to mark this pointer as readable for valgrind
VALGRIND_MAKE_MEM_DEFINED(&pcpy, sizeof(pcpy)); #endif
MarkBag(*pcpy, 0);
}
}
}
}
static NOINLINE void GenStackFuncBags(void)
{
Bag * top; // top of stack
Bag * p; // loop variable
#ifdef EMSCRIPTEN
emscripten_scan_stack(ScanRange);
emscripten_scan_registers(ScanRange); // The standard scanning may not be required with // emscripten, but it does not do any harm #endif
top = (Bag *)((void *)&top);
ScanRange(StackBottomBags, top);
// mark content of registers, dirty dirty hack: we treat the jmp_buf // as a sequence of Bag values. Note that sizeof(jmp_buf) need not // be a multiple of sizeof(Bag), hence the end condition looks // slightly. unusual. for (p = (Bag *)RegsBags;
p < (Bag *)((char *)RegsBags + sizeof(RegsBags)); p++)
MarkBag(*p, 0);
/* These are used to overwrite masterpointers which may still be linkedfromweakpointerobjectsbutwhosebagbodieshavebeen collected.Twovaluesareusedsothatoldmasterpointersofthis kindcanbereclaimedafterafullgarbagecollection.Thevaluesmust notlooklikevalidpointers,andshouldbecongruentto1modsizeof(Bag), toensurethatIsWeakDeadBagworkscorrectly.
*/
static Bag * NewWeakDeadBagMarker = (Bag *)(1000*sizeof(Bag) + 1); static Bag * OldWeakDeadBagMarker = (Bag *)(1001*sizeof(Bag) + 1);
static UInt CollectBags_Mark(UInt FullBags)
{ // prepare the list of marked bags for the future
MarkedBags = 0;
// mark from the static area for (int i = 0; i < GlobalBags.nr; i++)
MarkBag( *GlobalBags.addr[i], 0 );
/* allow installing a custom marking function. This is used for integrating GAP(possiblylinkedasasharedlibrary)withothercodebaseswhichuse theirownformofgarbagecollection.Forexample,withPython(for
SageMath) or Julia. */ if (ExtraMarkFuncBags) {
(*ExtraMarkFuncBags)();
}
// mark from the stack
_setjmp(RegsBags); #ifdefined(SYS_IS_SPARC)
SparcStackFuncBags(); #endif
GenStackFuncBags();
// mark the subbags of the changed old bags while ( ChangedBags != 0 ) { // extract the head from the linked list
Bag first = ChangedBags;
ChangedBags = LINK_BAG(first);
LINK_BAG(first) = first;
// mark subbags - we need to distinguish between young and old bags: // For old bags, we invoke the marking function for bags with the // given TNUM. // Young bags normally are never put onto the changed list, because // CHANGED_BAGS ignores young bags. However, it can happen if we // resize an old bag and it needs to be moved as a result, or if we // swap the masterpointers of an old and a young bag. In that case, // we must be careful to not collect the young bag (which was old // before the masterpointer swap; see the comment on // 'SwapMasterPoint' for a detailed explanation why that is so). To // facilitate this, 'SwapMasterPoint' forces that bag onto the // ChangedBags list. Then, we put such a young bag onto the list of // marked bags (via MarkBag), which ensures it is not collected. // // Note that it doesn't help to use 'MarkBag' on an old bags, as it // ignores old bags (which are always assumed to be marked). // Conversely, using TabMarkFuncBags on a young bag is no good, // because that function only puts subbags on the list of marked // bag, which does not prevent the young bag itself from being // collected (which is what we need). if (CONST_PTR_BAG(first) <= YoungBags)
(*TabMarkFuncBags[TNUM_BAG(first)])( first, 0 ); else
MarkBag(first, 0);
}
// tag all marked bags and mark their subbags
UInt nrLiveBags = 0;
UInt sizeLiveBags = 0; while ( MarkedBags != 0 ) { // extract the head from the linked list
Bag first = MarkedBags;
MarkedBags = LINK_BAG(first); // Gasman in some places treats as bag where // CONST_PTR_BAG(bag) == YoungBags as a young bag, and in other // places as an old bag. However, this is not a problem because // it is not possible for such a bag to exist. Sanity check // this condition.
GAP_ASSERT(CONST_PTR_BAG(first) != YoungBags);
// mark subbags
(*TabMarkFuncBags[TNUM_BAG(first)])( first, 0 );
// collect some statistics
nrLiveBags++;
sizeLiveBags += SIZE_BAG(first);
}
// information after the mark phase
NrLiveBags += nrLiveBags;
SyMsgsBags(FullBags, 1, nrLiveBags);
SizeLiveBags += sizeLiveBags;
SyMsgsBags(FullBags, 2, sizeLiveBags / 1024);
return nrLiveBags;
}
static UInt CollectBags_Sweep(UInt FullBags)
{
Bag * dst; // destination in sweeping
Bag * src; // source in sweeping
Bag * end; // end of a bag in sweeping
UInt nrDeadBags; // number of dead new bags
UInt nrHalfDeadBags; // number of half dead new bags
UInt sizeDeadBags; // total size of dead new bags
// sweep through the young generation
nrDeadBags = 0;
nrHalfDeadBags = 0;
sizeDeadBags = 0;
dst = YoungBags;
src = YoungBags; while ( src < AllocBags ) {
BagHeader * header = (BagHeader *)src;
// leftover of a resize of <n> bytes if ( header->type == T_DUMMY ) {
// copy data area if (TabSweepFuncBags[header->type] != 0) { // Call the installed sweeping function
(*(TabSweepFuncBags[header->type]))(DATA(header), dst, end - DATA(header));
dst += end - DATA(header);
}
// oops else {
Panic("Gasman found a bogus header");
}
}
// reset the pointer to the free storage
AllocBags = YoungBags = dst;
// clear the new free area
memset(dst, 0, ((Char *)src)-((Char *)dst));
// information after the sweep phase
NrDeadBags += nrDeadBags;
NrHalfDeadBags += nrHalfDeadBags;
SyMsgsBags(FullBags, 3, (FullBags ? NrDeadBags : nrDeadBags)); if ( FullBags )
NrDeadBags = 0;
SizeDeadBags += sizeDeadBags;
SyMsgsBags(FullBags, 4, (FullBags ? SizeDeadBags : sizeDeadBags) / 1024); if ( FullBags )
SizeDeadBags = 0;
return nrDeadBags + nrHalfDeadBags;
}
staticInt CollectBags_Check(UInt size, UInt FullBags, UInt nrBags)
{
UInt done; // do we have to make a full gc
Bag bag; // loop variable
UInt i; // loop variable
// Check if this allocation would even fit into memory if (SIZE_MAX - (size_t)(sizeof(BagHeader) + size) < (size_t)AllocBags) { return2; // signal error
}
// store in 'stopBags' where this allocation takes us
Bag * stopBags = AllocBags + WORDS_BAG(sizeof(BagHeader)+size);
// if we only performed a partial garbage collection if ( ! FullBags ) {
// maybe adjust the size of the allocation area if ( nrBags < 512
/* The test below should stop AllocSizeBags
growing uncontrollably when all bags are big */
&& stopBags > OldBags + 4*1024*WORDS_BAG(AllocSizeBags))
AllocSizeBags += 256; elseif ( 4096 < nrBags
&& 256 < AllocSizeBags )
AllocSizeBags -= 256;
// if we don't get enough free storage or masterpointers do full gc if ( EndBags < stopBags + WORDS_BAG(1024*AllocSizeBags)
|| SizeMptrsArea <
// nrBags+ 4096 /* If this test triggered, but the one below didn't thenafullcollectionwouldensuewhichwouldn't doanythinguseful.Possiblyaversionofthe abovetestshouldbemovedintothefullcollectionalso
but I wasn't sure it always made sense SL */
// change the test to avoid subtracting unsigned integers
// if we already performed a full garbage collection else {
/* Clean up old half-dead bags alsoreorderthefreemasterpointerlinkedlist
to get more locality */
FreeMptrBags = 0; for (bag = MptrBags; bag < MptrEndBags; bag++) {
Bag * mptr = bag->body; if ( mptr == OldWeakDeadBagMarker)
NrHalfDeadBags--; if (mptr == OldWeakDeadBagMarker || IS_BAG_ID(mptr) || mptr == 0) {
bag->body = FreeMptrBags;
FreeMptrBags = bag;
}
}
// get the storage we absolutely need if (EndBags < stopBags) {
size_t bytes = (char *)stopBags - (char *)EndBags; // Increment in blocks of 512K
size_t blocks = bytes / 1024 / 512; if (blocks * 1024 * 512 < bytes) {
blocks++;
} if (SyAllocBags(blocks * 512, 0)) {
EndBags += WORDS_BAG(blocks * 512 * 1024);
}
}
// if not enough storage is free, fail if ( EndBags < stopBags ) return2; // signal error
// if less than 1/8th is free, get more storage (in 1/2 MBytes) while ( ( SpaceBetweenPointers(EndBags, stopBags) < SpaceBetweenPointers(stopBags, OldBags)/7 ||
SpaceBetweenPointers(EndBags, stopBags) < WORDS_BAG(AllocSizeBags) )
&& SyAllocBags(512,0) )
EndBags += WORDS_BAG(512*1024L);
/* If we are having trouble, then cut our cap to fit our cloth *. if(EndBags-stopBags<AllocSizeBags)
AllocSizeBags = 7*(Endbags - stopBags)/8; */
// if less than 1/16th is free, prepare for an interrupt if (SpaceBetweenPointers(stopBags,MptrEndBags)/15 < SpaceBetweenPointers(EndBags,stopBags) ) { /*N 1993/05/16 martin must change 'gap.c' */
;
}
// if more than 1/8th is free, give back storage (in 1/2 MBytes) while (SpaceBetweenPointers(stopBags,MptrEndBags)/7 <= SpaceBetweenPointers(EndBags,stopBags)-WORDS_BAG(512*1024L)
&& SpaceBetweenPointers(EndBags,stopBags) > WORDS_BAG(AllocSizeBags) + WORDS_BAG(512*1024L)
&& SyFreeBags(512) )
EndBags -= WORDS_BAG(512*1024L);
#ifdef GAP_MEM_CHECK
UInt SpareMasterPointers = 100000; #else
UInt SpareMasterPointers = SpaceBetweenPointers(EndBags, stopBags)/7; #endif // if we want to increase the masterpointer area if ( SizeMptrsArea-NrLiveBags < SpareMasterPointers ) { // this is how many new masterpointers we want
i = SpareMasterPointers - (SizeMptrsArea-NrLiveBags); // move the bags area
SyMemmove(OldBags+i, OldBags, SizeAllBagsArea*sizeof(*OldBags));
// update the masterpointers for (bag = MptrBags; bag < MptrEndBags; bag++) { if (MptrEndBags <= (Bag)bag->body)
bag->body = (Bag)bag->body + i;
}
// link the new part of the masterpointer area for (bag = MptrEndBags; bag + 1 < MptrEndBags + i; bag++) {
bag->body = bag + 1;
}
bag->body = FreeMptrBags;
FreeMptrBags = MptrEndBags;
// call the before functions (if any) for (i = 0; i < CollectFuncBags.nrBefore; ++i)
CollectFuncBags.before[i]();
// do we want to make a full garbage collection? do { if (full) {
// then every bag is considered to be a young bag
YoungBags = OldBags;
NrLiveBags = 0;
SizeLiveBags = 0;
// empty the list of changed old bags while ( ChangedBags != 0 ) {
Bag first = ChangedBags;
ChangedBags = LINK_BAG(first);
LINK_BAG(first) = first;
}
// Also time to change the tag for dead children of weak pointer // objects. After this collection, there can be no more weak pointer // objects pointing to anything with OldWeakDeadBagMarker in it.
SWAP(Bag *, OldWeakDeadBagMarker, NewWeakDeadBagMarker);
}
// information at the beginning of garbage collections
SyMsgsBags(full, 0, 0);
// the variable done can take on several values: // 0: not yet finished, try again with a full collection // 1: finished successfully to allocate the request memory // 2: giving up, we are out of memory
// if there is another iteration of this loop, then we should perform // a full collection
full = 1;
} while (!done);
// call the after functions (if any) for (i = 0; i < CollectFuncBags.nrAfter; ++i)
CollectFuncBags.after[i]();
// iterate over all bag identifiers for (bag = MptrBags; bag < MptrEndBags; bag++) {
// weakly dead bag? if (bag->body == (Bag)NewWeakDeadBagMarker ||
bag->body == (Bag)OldWeakDeadBagMarker) continue;
// part of chain of free master pointers? if (bag->body == 0 || IS_BAG_ID(bag->body)) { continue;
}
// none of the above, so it must be an active master pointer // otherwise, error out if (!IS_BAG_BODY(bag->body))
Panic("Bad master pointer detected");
if (GET_MARK_BITS(LINK_BAG(bag))) {
Panic("Master pointer with Mark bits detected");
}
// sanity check: the link pointer must either point back; or else // this bag must be part of the chain of changed bags (which thus // must be non-empty) if (ChangedBags == 0 && LINK_BAG(bag) != bag) {
Panic("Master pointer with bad link word detected");
}
}
// check the chain of free master pointers
bag = FreeMptrBags; while (bag != 0) { if (!IS_BAG_ID(bag))
Panic("Bad chain of free master pointers detected");
bag = (Bag)bag->body;
}
}
// Swap the master pointers of bag1 and bag2 // // We need to make sure the correct bags are garbage collected, so we always put // *both* bags on the ChangedBags linked-list, rather than pick through the // exact cases, as it is never incorrect to mark something changed. // // For completeness and future reference here are the necessary points to // consider. // // When swapping two master pointers, we have to take into account whether the // bags they refer are on the ChangedBags list, as we otherwise may end up in an // inconsistent state, where a bag is referenced, but GASMAN does not know this. // GASMAN then collects this bag, resulting in a corrupted workspace. // // We consider the following three cases: // // 1. Both bags are old. Then if the original bag1 had been previously marked as // changed (by having been put into the ChangedBags singly linked list), then // we must make sure to mark the new bag2 as changed, too (and vice-versa). // // 2. Both bags are young. Then they typically will not be on the list of // changed bags, as CHANGED_BAGS just skips them. // However, while CHANGED_BAG will never put a young bag on the list of // changed bags, young bags can still be put on the ChangedBags list in // step 3, so we need to do something similar as in step 1. // // 3. bag1 is young and bag2 is old (or vice-versa), then after swapping, bag1 // is old and bag2 is young, as the 'young'ness moves with the contents, so // we must mark bag1 changed if bag2 was previously changed. // // More importantly, bag2 is now young, but the only references to bag2 might // be in an old bag, that is not marked changed. Thus bag2 would get // (incorrectly) collected, because these bags are not considered in a // garbage collection. // // To avoid this we force bag2 onto the ChangedBags list, but we can't use // CHANGED_BAG, as it skips young bags. // void SwapMasterPoint(Bag bag1, Bag bag2)
{ if (bag1 == bag2) return;
// First make sure both bags are in change list // We can't use CHANGED_BAG as it skips young bags if (LINK_BAG(bag1) == bag1) {
LINK_BAG(bag1) = ChangedBags;
ChangedBags = bag1;
} if (LINK_BAG(bag2) == bag2) {
LINK_BAG(bag2) = ChangedBags;
ChangedBags = bag2;
}
// get the pointers & swap them
SWAP(UInt *, bag1->body, bag2->body);
// Now swap links, so in the end the list will go // through the bags in the same order.
SWAP(Bag, LINK_BAG(bag1), LINK_BAG(bag2));
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.199 Sekunden
(vorverarbeitet am 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.