123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784 |
- //#define BF_GC_DISABLED
- //#define BF_GC_LOG_ENABLED
- #define _WIN32_WINNT _WIN32_WINNT_WIN8
- // This spits out interesting stats periodically to the console
- #define BF_GC_PRINTSTATS
- //TEMPORARY
- //#define BF_GC_DEBUGSWEEP
- //#define BF_GC_EMPTYSCAN
- // Disable efficient new mass-freeing in TCMalloc
- //#define BF_GC_USE_OLD_FREE
- #ifdef BF_DEBUG
- // Useful for tracing down memory corruption -- memory isn't returned to TCMalloc, it's just marked as freed. You'll run out eventually
- //#define BF_NO_FREE_MEMORY
- // Old and not too useful
- //#define BG_GC_TRACKPTRS
- #endif
- #if defined BF_OBJECT_TRACK_ALLOCNUM && defined BF_NO_FREE_MEMORY
- //#define BF_GC_VERIFY_SWEEP_IDS
- #endif
- //#define BF_SECTION_NURSERY
- #ifdef BF_PLATFORM_WINDOWS
- #include <direct.h>
- #endif
- #include "gc.h"
- #ifdef BF_GC_SUPPORTED
- #include <fstream>
- #include "BeefySysLib/Common.h"
- #include "BeefySysLib/BFApp.h"
- #include "BeefySysLib/util/CritSect.h"
- #include "BeefySysLib/util/BeefPerf.h"
- #include "BeefySysLib/util/HashSet.h"
- #include "BeefySysLib/util/Dictionary.h"
- #include <unordered_set>
- #include "../rt/BfObjects.h"
- #include "../rt/Thread.h"
- #ifdef BF_PLATFORM_WINDOWS
- #include <psapi.h>
- #endif
- #ifndef BF_TCMALLOC_DISABLED
- #define TCMALLOC_NO_MALLOCGUARD
- #define TCMALLOC_NAMESPACE tcmalloc_obj
- #define TCMALLOC_EXTERN static
- #include "gperftools/src/tcmalloc.cc"
- #else
- #define tc_malloc malloc
- #define tc_free free
- #endif
- USING_NS_BF;
- //System::Threading::Thread* gMainThread;
- #include "BeefySysLib/util/PerfTimer.h"
- /*#include "BeefyRtCPP/MonitorManager.h"
- #include "BeefyRtCPP/InternalThread.h"
- #include "BeefyRtCPP/ReflectionData.h"
- #include "BeefyRtCPP/RtCommon_inl.h"
- #include "BeefyRtCPP/BFArray.h"
- #include "BeefyRtCPP/Reflection.h"*/
- extern "C" GCDbgData gGCDbgData = { 0 };
- void BfLog(const char* fmt ...)
- {
- static int lineNum = 0;
- lineNum++;
- static FILE* fp = fopen("dbg_bf.txt", "wb");
- va_list argList;
- va_start(argList, fmt);
- String aResult = vformat(fmt, argList);
- va_end(argList);
- aResult = StrFormat("%d ", lineNum) + aResult;
- fwrite(aResult.c_str(), 1, aResult.length(), fp);
- fflush(fp);
- }
- //////////////////////////////////////////////////////////////////////////
- using Beefy::CritSect;
- BF_TLS_DECLSPEC BFGC::ThreadInfo* BFGC::ThreadInfo::sCurThreadInfo;
- HANDLE gGCHeap = 0;
- #ifdef _DEBUG
- bool gGCGetAllocStats = true;
- #else
- bool gGCGetAllocStats = false;
- #endif
- #ifdef BF_GC_VERIFY_SWEEP_IDS
- static std::set<int> allocIdSet;
- static int maxAllocNum = 0;
- #endif
- int gGCAllocCount = 0;
- int gGCAllocBytes = 0;
- std::vector<int> gGCAllocCountMap;
- std::vector<int> gGCAllocSizeMap;
- struct TCMallocRecord
- {
- void* mPtr;
- int mSize;
- };
- static std::vector<TCMallocRecord> gTCMallocRecords;
- void TCMalloc_RecordAlloc(void* ptr, int size)
- {
- TCMallocRecord mallocRecord = { ptr, size };
- gTCMallocRecords.push_back(mallocRecord);
- }
- static void TCMalloc_FreeAllocs()
- {
- for (auto& record : gTCMallocRecords)
- {
- ::VirtualFree(record.mPtr, 0, MEM_RELEASE);
- }
- gTCMallocRecords.clear();
- }
- void PatchWindowsFunctions()
- {
- // For TCMalloc, don't really patch
- }
- #ifdef BG_GC_TRACKPTRS
- boost::unordered_set<void*> gTrackPtr;
- #endif
- void BFMarkThreadPoolJobs()
- {
- }
- #ifndef BF_MONOTOUCH
- void BFMarkCOMObjects()
- {
- }
- void BFIMarkStackData(BFIThreadData* bfiThreadData)
- {
- }
- #endif
- /*void* sDbgPtr0 = NULL;
- void* sDbgPtr1 = NULL;
- void* sDbgPtr2 = NULL;*/
- void BFGC::MarkMembers(bf::System::Object* obj)
- {
- //BP_ZONE("MarkMembers");
- /*if ((obj == sDbgPtr0) || (obj == sDbgPtr1) || (obj == sDbgPtr2))
- {
- int a = 0;
- }*/
- if (((obj->mObjectFlags & BF_OBJECTFLAG_DELETED) != 0) && (!mMarkingDeleted))
- {
- mMarkingDeleted = true;
- gBfRtDbgCallbacks.Object_GCMarkMembers(obj);
- mMarkingDeleted = false;
- }
- else
- {
- gBfRtDbgCallbacks.Object_GCMarkMembers(obj);
- }
- }
- static void MarkObject(bf::System::Object* obj)
- {
- if ((obj != NULL) && ((obj->mObjectFlags & (BF_OBJECTFLAG_MARK_ID_MASK)) != BFGC::sCurMarkId))
- gBFGC.MarkFromGCThread(obj);
- }
- //////////////////////////////////////////////////////////////////////////
- //AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
- namespace tcmalloc
- {
- extern "C" int RunningOnValgrind(void)
- {
- return 0;
- }
- }
- ////////////////////////////////////////////
- BFGC::ThreadInfo::~ThreadInfo()
- {
- if (mThreadHandle != NULL)
- BfpThread_Release(mThreadHandle);
- }
- //////////////////////////////////////////////////////////////////////////
- #ifdef BF_GC_LOG_ENABLED
- class GCLog
- {
- public:
- enum
- {
- EVENT_ALLOC,
- EVENT_GC_START, // cycle#
- EVENT_GC_UNFREEZE, // cycle#
- EVENT_MARK,
- EVENT_WB_MARK,
- EVENT_THREAD_STARTED,
- EVENT_THREAD_DONE,
- EVENT_SCAN_THREAD,
- EVENT_CONSERVATIVE_SCAN,
- EVENT_FOUND_TARGET,
- EVENT_FINALIZE_LIST,
- EVENT_LEAK,
- EVENT_DELETE,
- EVENT_FREE,
- EVENT_WB_MOVE,
- EVENT_WEAK_REF,
- EVENT_STRONG_REF,
- EVENT_WEAKREF_MARKED,
- EVENT_WEAK,
- EVENT_RESURRECT,
- EVENT_WAS_RESURRECT,
- EVENT_GC_DONE
- };
- class Entry
- {
- public:
- int mEvent;
- intptr mParam1;
- intptr mParam2;
- intptr mParam3;
- };
- Beefy::CritSect mCritSect;
- static const int BUFFSIZE = 1024*1024;
- static const int BUFFSIZE_MASK = BUFFSIZE-1;
- Entry mBuffer[BUFFSIZE];
- volatile int mHead;
- volatile int mTail;
- volatile bool mWriting;
- public:
- GCLog()
- {
- mWriting = false;
- }
- void Log(int event, intptr param1 = 0, intptr param2 = 0, intptr param3 = 0)
- {
- if (mWriting)
- {
- // Not a strict guarantee, but should keep us from only messing up
- // more than a single element of the log while we're writing it
- Beefy::AutoCrit autoCrit(mCritSect);
- return;
- }
- BF_FULL_MEMORY_FENCE();
- Entry entry = {event, param1, param2, param3};
-
- int prevHead;
- int nextHead;
- while (true)
- {
- prevHead = mHead;
- nextHead = (mHead + 1) & BUFFSIZE_MASK;
- if (nextHead == mTail)
- {
- int prevTail = mTail;
- int nextTail = (mTail + 1) & BUFFSIZE_MASK;
- if (::InterlockedCompareExchange((uint32*)&mTail, nextTail, prevTail) != prevTail)
- continue; // Try again
- }
- if (::InterlockedCompareExchange((uint32*)&mHead, nextHead, prevHead) == prevHead)
- break;
- }
-
- mBuffer[prevHead] = entry;
- }
- const char* GetNameStr(bf::System::Type* type)
- {
- static Beefy::String str;
- if (type == NULL)
- return "NULL";
- try
- {
- bf::System::Type* bfTypeRoot = (bf::System::Type*)type;
- str = type->GetFullName();
- return str.c_str();
- }
- catch (...)
- {
- return "<invalid>";
- }
- }
- #pragma warning(disable:4477)
- void Write()
- {
- Beefy::AutoCrit autoCrit(mCritSect);
- wchar_t str[MAX_PATH];
- GetCurrentDirectoryW(MAX_PATH, str);
- FILE* fp = fopen("c:\\temp\\gclog.txt", "w");
-
- mWriting = true;
- int sampleHead = mHead;
- BF_FULL_MEMORY_FENCE();
- int sampleTail = mTail;
- BF_FULL_MEMORY_FENCE();
- int pos = sampleTail;
- while (pos != sampleHead)
- {
- Entry ent = mBuffer[pos];
-
- switch (ent.mEvent)
- {
- case EVENT_ALLOC:
- fprintf(fp, "Alloc:%p Type:%s\n", ent.mParam1, GetNameStr((bf::System::Type*)ent.mParam2));
- break;
- case EVENT_GC_START:
- fprintf(fp, "GCStart MarkId:%d Tick:%d\n", ent.mParam1, ent.mParam2);
- break;
- case EVENT_GC_UNFREEZE:
- fprintf(fp, "GCUnfreeze MarkId:%d Tick:%d\n", ent.mParam1, ent.mParam2);
- break;
- case EVENT_MARK:
- fprintf(fp, "GCMark Obj:%p Flags:%X Parent:%p\n", ent.mParam1, ent.mParam2, ent.mParam3);
- break;
- case EVENT_WB_MARK:
- fprintf(fp, "WriteBarrierMark Obj:%p Type:%s Thread:%p\n", ent.mParam1, GetNameStr((bf::System::Type*)ent.mParam2), ent.mParam3);
- break;
- case EVENT_WB_MOVE:
- fprintf(fp, "WriteBarrier Entry Move Obj:%p Type:%s Thread:%p\n", ent.mParam1, GetNameStr((bf::System::Type*) ent.mParam2), ent.mParam3);
- break;
- case EVENT_FOUND_TARGET:
- fprintf(fp, "Heap Scan Found Target:%p Type:%s Flags:%d\n", ent.mParam1, GetNameStr((bf::System::Type*)ent.mParam2), ent.mParam3);
- break;
- case EVENT_FINALIZE_LIST:
- fprintf(fp, "On Finalize List:%p\n", ent.mParam1);
- break;
- case EVENT_LEAK:
- fprintf(fp, "Leak Detected:%p Type:%s\n", ent.mParam1, GetNameStr((bf::System::Type*)ent.mParam2));
- break;
- case EVENT_FREE:
- fprintf(fp, "Free Obj:%p\n", ent.mParam1);
- break;
- case EVENT_DELETE:
- fprintf(fp, "Delete Obj:%p\n", ent.mParam1);
- break;
- case EVENT_THREAD_STARTED:
- fprintf(fp, "ThreadStarted:%p TID:%d\n", ent.mParam1, ent.mParam2);
- break;
- case EVENT_THREAD_DONE:
- fprintf(fp, "ThreadDone:%p\n", ent.mParam1);
- break;
- case EVENT_SCAN_THREAD:
- fprintf(fp, "ScanThread:%p Id:%d\n", ent.mParam1, ent.mParam2);
- break;
- case EVENT_CONSERVATIVE_SCAN:
- fprintf(fp, "ConservativeScan:%p to %p\n", ent.mParam1, ent.mParam2);
- break;
- case EVENT_GC_DONE:
- fprintf(fp, "GCDone\n");
- break;
- default:
- BF_FATAL("Unknown event");
- break;
- }
-
- pos = (pos + 1) & BUFFSIZE_MASK;
- }
-
- fclose(fp);
- mWriting = false;
- }
- };
- GCLog gGCLog;
- void BFGCLogWrite()
- {
- gGCLog.Write();
- }
- #define BFLOG(cmd) gGCLog.Log(cmd)
- #define BFLOG1(cmd, param1) gGCLog.Log(cmd, param1)
- #define BFLOG2(cmd, param1, param2) gGCLog.Log(cmd, param1, param2)
- #define BFLOG3(cmd, param1, param2, param3) gGCLog.Log(cmd, param1, param2, param3)
- #else
- #define BFLOG(cmd)
- #define BFLOG1(cmd, param1)
- #define BFLOG2(cmd, param1, param2)
- #define BFLOG3(cmd, param1, param2, param3)
- void BFGCLogWrite() {}
- #endif
- void BFGCLogAlloc(bf::System::Object* obj, bf::System::Type* objType, int allocNum)
- {
- BFLOG3(GCLog::EVENT_ALLOC, (intptr)obj, (intptr)objType, allocNum);
- }
- BfDbgInternalThread* GetCurrentInternalThread()
- {
- return NULL;
- }
- ////////////////////////////////////////////
- static void CheckTcIntegrity()
- {
- auto pageHeap = Static::pageheap();
- if (pageHeap == NULL)
- return;
- #ifdef BF64
- //BP_ZONE("CheckTcIntegrity");
- Beefy::HashSet<tcmalloc_obj::Span*> spanSet;
- int interiorLen = PageHeap::PageMap::INTERIOR_LENGTH;
- int leafLen = PageHeap::PageMap::LEAF_LENGTH;
- for (int pageIdx1 = 0; pageIdx1 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx1++)
- {
- PageHeap::PageMap::Node* node1 = Static::pageheap()->pagemap_.root_->ptrs[pageIdx1];
- if (node1 == NULL)
- continue;
- for (int pageIdx2 = 0; pageIdx2 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx2++)
- {
- PageHeap::PageMap::Node* node2 = node1->ptrs[pageIdx2];
- if (node2 == NULL)
- continue;
- for (int pageIdx3 = 0; pageIdx3 < PageHeap::PageMap::LEAF_LENGTH; pageIdx3++)
- {
- tcmalloc_obj::Span* span = (tcmalloc_obj::Span*)node2->ptrs[pageIdx3];
- if (span != NULL)
- {
- int expectedStartPage = ((pageIdx1 * PageHeap::PageMap::INTERIOR_LENGTH) + pageIdx2) * PageHeap::PageMap::LEAF_LENGTH + pageIdx3;
- if (span->start == expectedStartPage)
- {
- auto result = spanSet.Add(span);
- BF_ASSERT(result);
- }
- }
- }
- }
- }
- int spansFound = spanSet.size();
- #endif
- }
- ////////////////////////////////////////////
- void BFCheckSuspended()
- {
- BfInternalThread* internalThread = (BfInternalThread*)GetCurrentInternalThread();
- if ((internalThread != NULL) && (internalThread->mIsSuspended))
- {
- internalThread = (BfInternalThread*)GetCurrentInternalThread();
- printf("IsSuspended\n");
- }
- BF_ASSERT((internalThread == NULL) || (!internalThread->mIsSuspended));
- }
- void* BfObjectAllocate(intptr size, bf::System::Type* objType)
- {
- bf::System::Object* obj = (bf::System::Object*)tc_malloc(size);
- BFLOG2(GCLog::EVENT_ALLOC, (intptr)obj, (intptr)objType);
- //CheckTcIntegrity();
- gBFGC.mBytesRequested += size;
- // #ifdef BF_GC_PRINTSTATS
- // ::InterlockedIncrement((volatile uint32*)&gBFGC.mTotalAllocs);
- // #endif
-
- #ifdef BG_GC_TRACKPTRS
- {
- Beefy::AutoCrit autoCrit(gBFGC.mCritSect);
- BF_LOGASSERT(gTrackPtr.find(obj) == gTrackPtr.end());
- gTrackPtr.insert(obj);
- }
- #endif
-
- #ifdef _DEBUG
- BF_LOGASSERT((obj->mAllocCheckPtr == 0) || (size > kMaxSize));
- #endif
- //memset((void*)obj, 0, size);
- #ifdef BF_OBJECT_TRACK_ALLOCNUM
- obj->mAllocNum = (int)::InterlockedIncrement((volatile uint32*) &BfObject::sCurAllocNum);
- #endif
-
- #ifdef BF_SECTION_NURSERY
- BfInternalThread* internalThread = (BfInternalThread*)bf::System::Threading::Thread::CurrentInternalThread_internal();
- if ((internalThread != NULL) && (internalThread->mSectionDepth == 1))
- internalThread->mSectionNursery.PushUnsafe(obj);
- #endif
-
- gBFGC.mAllocSinceLastGC += size;
- /*if ((uint32)gBFGC.mAllocSinceLastGC > (uint32)gBFGC.mAllocGCTrigger)
- gBFGC.TriggerCollection();*/
- //BfLog("AllocId %d ptr: %p Thread:%d\n", gBFGC.mTotalAllocs, obj, GetCurrentThreadId());
- //gBFGC.TriggerCollection(true);
- return obj;
- }
- //////////////////////////////////////////////////////////////////////////
- #define BF_GC_MAX_PENDING_OBJECT_COUNT 16*1024
- ///
- ///
- class BFFinalizeData
- {
- public:
- int mFinalizeCount;
- bool mInFinalizeList;
- public:
- BFFinalizeData()
- {
- mFinalizeCount = 1;
- mInFinalizeList = false;
- }
- };
- ///
- BFGC gBFGC;
- int volatile BFGC::sCurMarkId = 1;
- int volatile BFGC::sAllocFlags = 1;
- int gBFCRTAllocSize = 0;
- BFGC::BFGC()
- {
- mRunning = false;
- mExiting = false;
- mPaused = false;
- mShutdown = false;
- mForceDecommit = false;
- mLastCollectFrame = 0;
- mSkipMark = false;
- mGracelessShutdown = false;
- mMainThreadTLSPtr = NULL;
-
- mCollectIdx = 0;
- mStackScanIdx = 0;
- mMarkDepthCount = 0;
- mMarkingDeleted = false;
- mCurMutatorMarkCount = 0;
- mCurGCMarkCount = 0;
- mCurFreedBytes = 0;
- sCurMarkId = 1;
- mQueueMarkObjects = false;
- //mEphemeronTombstone = NULL;
- mWaitingForGC = false;
- mCollectRequested = false;
- mAllocSinceLastGC = 0;
- mFullGCTriggered = false;
- mDebugDumpState = 0;
- mCurScanIdx = 1;
- mTotalAllocs = 0;
- mTotalFrees = 0;
- mLastFreeCount = 0;
- mBytesFreed = 0;
- mBytesRequested = 0;
- mRequestedSizesInvalid = false;
- mDisplayFreedObjects = false;
- mHadRootError = false;
- mCurLiveObjectCount = 0;
- mFreeSinceLastGC = 0;
- // Default to just over two 60Hz frames
- // This keeps stack scanning from adding excessive costs to a 30Hz app, or
- // from impacting two successive frames in a triple-buffered 60Hz app where
- // only one slow frame could be absorbed
- mMultiStackScanWait = 35;
- // By default we collect after freeing 64MB
- mFreeTrigger = 64*1024*1024;
- mMaxPausePercentage = 20;
- mMaxRawDeferredObjectFreePercentage = 30;
-
- // Zero means to run continuously. -1 means don't trigger on a time base
- // Defaults to collecting every 2 seconds
- mFullGCPeriod = 2000;
- mGCThread = NULL;
- gGCDbgData.mDbgFlags = gBfRtDbgFlags;
- ThreadCache::InitTSD();
- if (UNLIKELY(Static::pageheap() == NULL)) ThreadCache::InitModule();
- gGCDbgData.mObjRootPtr = Static::pageheap()->pagemap_.root_;
- for (int i = 0; i < kNumClasses; i++)
- gGCDbgData.mSizeClasses[i] = Static::sizemap()->ByteSizeForClass(i);
- RawInit();
- }
- BFGC::~BFGC()
- {
- //::HeapDestroy(gGCHeap);
- //gGCHeap = NULL;
- Beefy::AutoCrit autoCrit(mCritSect);
- for (auto thread : mThreadList)
- {
- delete thread;
- }
- }
- /*inline T BFWB(typename BFToType<T>::type::BFBaseObjectInObject obj)
- {
- if ((obj != NULL) && ((obj->mBFMonitorPtrAndObjFlags & (BF_OBJECTFLAG_MARK_ID_MASK)) != BFGC::sCurMarkId))
- gBFGC.MarkFromMutator(obj, 0);
- return (T) obj;
- }*/
- /*void BFGC::RegisterRoot(bf::System::Object* obj)
- {
- Beefy::AutoCrit autoCrit(mCritSect);
- mExplicitRoots.push_back(obj);
- if (mRunning)
- BFWB(obj);
- }*/
- #ifdef BF32
- static tcmalloc_obj::Span* TCGetSpanAt(void* addr)
- {
- int checkPageId = (int)((uintptr)addr >> kPageShift);
- int checkRootIdx = checkPageId >> PageHeap::PageMap::LEAF_BITS;
- int checkLeafIdx = checkPageId & (PageHeap::PageMap::LEAF_LENGTH - 1);
- PageHeap::PageMap::Leaf* rootLeaf = Static::pageheap()->pagemap_.root_[checkRootIdx];
- if (rootLeaf == NULL)
- return NULL;
- auto span = (tcmalloc_obj::Span*)rootLeaf->values[checkLeafIdx];
- intptr pageSize = (intptr)1 << kPageShift;
- int spanSize = pageSize * span->length;
- void* spanStart = (void*)((intptr)span->start << kPageShift);
- void* spanEnd = (void*)((intptr)spanStart + spanSize);
- if ((addr >= spanStart) && (addr < spanEnd))
- return span;
- return span;
- }
- #else
- static tcmalloc_obj::Span* TCGetSpanAt(void* addr)
- {
- int64 checkPageId = (int64)((uintptr)addr >> kPageShift);
- int pageIdx1 = (int)(checkPageId >> (PageHeap::PageMap::LEAF_BITS + PageHeap::PageMap::INTERIOR_BITS));
- int pageIdx2 = (int)((checkPageId >> PageHeap::PageMap::LEAF_BITS) & (PageHeap::PageMap::INTERIOR_LENGTH-1));
- int pageIdx3 = (int)(checkPageId & (PageHeap::PageMap::LEAF_LENGTH-1));
- PageHeap::PageMap::Node* node1 = Static::pageheap()->pagemap_.root_->ptrs[pageIdx1];
- if (node1 == NULL)
- return NULL;
- PageHeap::PageMap::Node* node2 = node1->ptrs[pageIdx2];
- if (node2 == NULL)
- return NULL;
- auto span = (tcmalloc_obj::Span*)node2->ptrs[pageIdx3];
- return span;
- // if (span == NULL)
- // return NULL;
- // intptr pageSize = (intptr)1 << kPageShift;
- // int spanSize = pageSize * span->length;
- // void* spanStart = (void*)((intptr)span->start << kPageShift);
- // void* spanEnd = (void*)((intptr)spanStart + spanSize);
- // if ((addr >= spanStart) && (addr < spanEnd))
- // return span;
- // return NULL;
- }
- #endif
- int gTargetFoundCount = 0;
- int gMarkTargetCount = 0;
- int gMarkTargetMutatorCount = 0;
- int BFGetObjectSize(bf::System::Object* obj)
- {
- // auto span = TCGetSpanAt(obj);
- // int elementSize = Static::sizemap()->ByteSizeForClass(span->sizeclass);
- // if (elementSize != 0)
- // return elementSize;
- // intptr pageSize = (intptr)1 << kPageShift;
- // int spanSize = pageSize * span->length;
- // return spanSize;
- const PageID p = reinterpret_cast<uintptr_t>(obj) >> kPageShift;
- size_t cl = Static::pageheap()->GetSizeClassIfCached(p);
- int allocSize = 0;
- if (cl == 0)
- {
- auto span = Static::pageheap()->GetDescriptor(p);
- if (span != NULL)
- {
- cl = span->sizeclass;
- if (cl == 0)
- {
- allocSize = span->length << kPageShift;
- }
- }
- }
- if (cl != 0)
- allocSize = Static::sizemap()->class_to_size(cl);
- return allocSize;
- }
- void BFCheckObjectSize(bf::System::Object* obj, int size)
- {
- if (gBFGC.mRunning)
- {
- BF_ASSERT(BFGetObjectSize(obj) == size);
- }
- }
- void BFGC::ConservativeScan(void* startAddr, int length)
- {
- if ((gBfRtDbgFlags & BfRtFlags_ObjectHasDebugFlags) == 0)
- return;
- BFLOG2(GCLog::EVENT_CONSERVATIVE_SCAN, (intptr)startAddr, (intptr)startAddr + length);
-
- void* ptr = (void*)((intptr)startAddr & ~((sizeof(intptr)-1)));
- void* endAddr = (uint8*)startAddr + length;
- while (ptr < endAddr)
- {
- void* addr = *(void**)ptr;
- MarkFromGCThread((bf::System::Object*)addr);
- ptr = (uint8*)ptr + sizeof(intptr);
- }
- }
- static tcmalloc_obj::Span* gLastSpan = NULL;
- bool BFGC::IsHeapObject(bf::System::Object* obj)
- {
- //BP_ZONE("IsHeapObject");
- if ((obj >= tcmalloc_obj::PageHeap::sAddressStart) && (obj < tcmalloc_obj::PageHeap::sAddressEnd))
- {
- tcmalloc_obj::Span* span = TCGetSpanAt(obj);
- gLastSpan = span;
- return span != NULL;
- }
- return false;
- }
- /*void BFGC::MarkTypeStatics(BFTypeRoot* checkType)
- {
- BFTypeRoot* innerCheckType = (BFTypeRoot*)checkType->mFirstNestedType;
- while (innerCheckType != NULL)
- {
- MarkTypeStatics(innerCheckType);
- innerCheckType = (BFTypeRoot*)innerCheckType->mNextSibling;
- }
- if (checkType->mTypeRootData->mTypeFlags & BF_TYPEFLAG_GENERIC_DEF_TYPE)
- return;
- if (checkType->mTypeRootData->mBFMarkStatics != NULL)
- checkType->mTypeRootData->mBFMarkStatics();
- }
- void BFGC::MarkStatics()
- {
- BFMark(bf::System::Object::sTypeLockObject);
- BFAppDomain* bfDomain = BFAppDomain::GetAppDomain();
- for (int assemblyIdx = 0; assemblyIdx < (int) bfDomain->mBFAssemblyVector.size(); assemblyIdx++)
- {
- BFAssembly* assembly = bfDomain->mBFAssemblyVector[assemblyIdx];
- BFTypeRoot* outerCheckType = (BFTypeRoot*)assembly->mFirstType;
- while (outerCheckType != NULL)
- {
- MarkTypeStatics(outerCheckType);
- outerCheckType = (BFTypeRoot*)outerCheckType->mNextSibling;
- }
- }
- BFType::BFMarkStatics();
- }*/
- void BFGC::MarkStatics()
- {
- bf::System::GC::DoMarkAllStaticMembers();
- //MarkObject(gMainThread);
- }
- void BFGC::ObjectDeleteRequested(bf::System::Object* obj)
- {
- if (mFreeTrigger >= 0)
- {
- int objSize = BFGetObjectSize(obj);
- if (BfpSystem_InterlockedExchangeAdd32((uint32*)&mFreeSinceLastGC, (uint32)objSize) + objSize >= mFreeTrigger)
- {
- mFreeSinceLastGC = 0;
- Collect(true);
- }
- BFLOG1(GCLog::EVENT_DELETE, (intptr)obj);
- }
- }
- bool BFGC::HandlePendingGCData(Beefy::Array<bf::System::Object*>* pendingGCData)
- {
- int count = 0;
-
- while (!pendingGCData->IsEmpty())
- {
- bf::System::Object* obj = pendingGCData->back();
- pendingGCData->pop_back();
- MarkMembers(obj);
- count++;
- }
- return count > 0;
- }
- bool BFGC::HandlePendingGCData()
- {
- bool didMark = false;
- didMark = HandlePendingGCData(&mPendingGCData);
- return didMark;
- }
- void BFGC::SweepSpan(tcmalloc_obj::Span* span, int expectedStartPage)
- {
- if ((gBfRtDbgFlags & BfRtFlags_ObjectHasDebugFlags) == 0)
- return;
- if (span->location != tcmalloc_obj::Span::IN_USE)
- return;
- if (span->start != expectedStartPage)
- {
- // This check covers when a new multi-page span is being put into place
- // and we catch after the first block, and it also catches the case
- // when the allocator splits a span and the pagemap can hold a reference
- // to a span that no longer covers that location.
- // For both of these cases we ignore the span. Remember, the worst case
- // here is that we'll miss a sweep of an object, which would just delay it's
- // cleanup until next GC cycle. Because the GC is the sole freer of spans,
- // there can never be a case where we find a valid span and then the span
- // changes sizeclass or location before we can scan the memory it points to.
- //
- // This also covers the case where a page spans over a radix map section and
- // we catch it on an outer loop again
- return;
- }
- intptr pageSize = (intptr)1<<kPageShift;
- int spanSize = pageSize * span->length;
- void* spanStart = (void*)((intptr)span->start << kPageShift);
- void* spanEnd = (void*)((intptr)spanStart + spanSize);
- void* spanPtr = spanStart;
- BF_LOGASSERT((spanStart >= tcmalloc_obj::PageHeap::sAddressStart) && (spanEnd <= tcmalloc_obj::PageHeap::sAddressEnd));
-
- int elementSize = Static::sizemap()->ByteSizeForClass(span->sizeclass);
- if (elementSize == 0)
- elementSize = spanSize;
- BF_LOGASSERT(elementSize >= sizeof(bf::System::Object));
-
- while (spanPtr <= (uint8*)spanEnd - elementSize)
- {
- //objCheckCount++;
- bf::System::Object* obj = (bf::System::Object*)spanPtr;
- #ifdef TARGET_TYPE
- if ((obj->mAllocCheckPtr != 0) && (obj->mBFVData->mType == TARGET_TYPE))
- {
- //sweepFoundCount++;
- }
- #endif
- // Mark 0 means 'just allocated'. 'deleteMarkId' is the last one. 'invalidMarkId' should be impossible because we'd either be deleted or marked as leaked before
- int deleteMarkId = mCurMarkId - 1;
- if (deleteMarkId == 0)
- deleteMarkId = 3;
-
- int invalidMarkId = deleteMarkId - 1;
- if (invalidMarkId == 0)
- invalidMarkId = 3;
- bool showAllAsLeaks = !mRunning;
- if (obj->mAllocCheckPtr != 0)
- {
- #ifdef BF_GC_VERIFY_SWEEP_IDS
- BF_LOGASSERT(obj->mAllocNum != 0);
- BF_LOGASSERT(allocIdSet.find(obj->mAllocNum) == allocIdSet.end());
- allocIdSet.insert(obj->mAllocNum);
- #endif
-
- mCurSweepFoundCount++;
- int objectFlags = obj->mObjectFlags;
- if (objectFlags == 0)
- mCurSweepFoundPermanentCount++;
-
- int markId = objectFlags & BF_OBJECTFLAG_MARK_ID_MASK;
- BF_ASSERT(markId != invalidMarkId);
- if (markId == 0)
- {
- // Newly-allocated! Set mark flag now...
- //obj->mObjectFlags = (BfObjectFlags)(obj->mObjectFlags | mCurMarkId);
- // Newly-allocated! Ignore, it will have its mark set soon (rare race condition)
- }
- else if ((markId == deleteMarkId) || (mSweepInfo.mShowAllAsLeaks))
- {
- if ((objectFlags & BF_OBJECTFLAG_DELETED) == 0)
- {
- if (!mSweepInfo.mEmptyScan)
- {
- // We set this to cause an error like:
- // Deleting an object that was detected as leaked (internal error)
- obj->mObjectFlags = (BfObjectFlags)(objectFlags | BF_OBJECTFLAG_STACK_ALLOC);
- if (!mHadRootError)
- {
- mSweepInfo.mLeakCount++;
- if (mSweepInfo.mLeakCount <= 1024 * 1024) // Have SOME limit
- {
- mSweepInfo.mLeakObjects.push_back(obj);
- }
- BFLOG2(GCLog::EVENT_LEAK, (intptr)obj, (intptr)obj->_GetType());
- #ifdef BF_GC_LOG_ENABLED
- gGCLog.Write();
- #endif
- }
- }
- }
- else
- {
- if (!mSweepInfo.mEmptyScan)
- {
- BFLOG1(GCLog::EVENT_FINALIZE_LIST, (intptr)obj);
- //obj->mObjectFlags = (BfObjectFlags) (obj->mObjectFlags & ~BF_OBJECTFLAG_FINALIZE_MAP);
- mFinalizeList.push_back(obj);
- }
- }
- }
- else
- mCurLiveObjectCount++;
- }
- spanPtr = (void*)((intptr)spanPtr + elementSize);
- }
- }
- void BFGC::Sweep()
- {
- BP_ZONE("Sweep");
- mCurLiveObjectCount = 0;
- auto pageHeap = Static::pageheap();
- if (pageHeap == NULL)
- return;
- #ifdef BF_GC_VERIFY_SWEEP_IDS
- maxAllocNum = bf::System::Object::sCurAllocNum;
- allocIdSet.clear();
- #endif
- int leafCheckCount = 0;
- int bits = kAddressBits;
- int leafLen = PageHeap::PageMap::LEAF_LENGTH;
- #ifdef BF32
- for (int rootIdx = 0; rootIdx < PageHeap::PageMap::ROOT_LENGTH; rootIdx++)
- {
- PageHeap::PageMap::Leaf* rootLeaf = Static::pageheap()->pagemap_.root_[rootIdx];
- if (rootLeaf == NULL)
- continue;
- for (int leafIdx = 0; leafIdx < PageHeap::PageMap::LEAF_LENGTH; leafIdx++)
- {
- leafCheckCount++;
- tcmalloc_obj::Span* span = (tcmalloc_obj::Span*)rootLeaf->values[leafIdx];
- if (span != NULL)
- {
- int expectedStartPage = (rootIdx * PageHeap::PageMap::LEAF_LENGTH) + leafIdx;
- SweepSpan(span, expectedStartPage);
- // We may be tempted to advance by span->length here, BUT
- // let us just scan all leafs becuause span data is
- // sometimes invalid and a long invalid span can cause
- // us to skip over an actual valid span
- }
- }
- }
- #else
- int interiorLen = PageHeap::PageMap::INTERIOR_LENGTH;
- for (int pageIdx1 = 0; pageIdx1 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx1++)
- {
- PageHeap::PageMap::Node* node1 = Static::pageheap()->pagemap_.root_->ptrs[pageIdx1];
- if (node1 == NULL)
- continue;
- for (int pageIdx2 = 0; pageIdx2 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx2++)
- {
- PageHeap::PageMap::Node* node2 = node1->ptrs[pageIdx2];
- if (node2 == NULL)
- continue;
- for (int pageIdx3 = 0; pageIdx3 < PageHeap::PageMap::LEAF_LENGTH; pageIdx3++)
- {
- leafCheckCount++;
- tcmalloc_obj::Span* span = (tcmalloc_obj::Span*)node2->ptrs[pageIdx3];
- if (span != NULL)
- {
- int expectedStartPage = ((pageIdx1 * PageHeap::PageMap::INTERIOR_LENGTH) + pageIdx2) * PageHeap::PageMap::LEAF_LENGTH + pageIdx3;
- SweepSpan(span, expectedStartPage);
- // We may be tempted to advance by span->length here, BUT
- // let us just scan all leafs becuause span data is
- // sometimes invalid and a long invalid span can cause
- // us to skip over an actual valid span
- }
- }
- }
- }
- #endif
- #ifdef BF_GC_VERIFY_SWEEP_IDS
- for (int allocNum = 1; allocNum < maxAllocNum; allocNum++)
- {
- BF_LOGASSERT(allocIdSet.find(allocNum) != allocIdSet.end());
- }
- #endif
- }
- extern Beefy::StringT<0> gDbgErrorString;
- void BFGC::ProcessSweepInfo()
- {
- if (mSweepInfo.mLeakCount > 0)
- {
- if (mSweepInfo.mShowAllAsLeaks)
- {
- // We aren't certain of the mark flags, so force the issue
- mCurMarkId = 0;
- for (auto obj : mSweepInfo.mLeakObjects)
- {
- obj->mObjectFlags = (BfObjectFlags)((obj->mObjectFlags & ~BF_OBJECTFLAG_MARK_ID_MASK) | mCurMarkId);
- }
- mCurMarkId = 1;
- }
- for (auto obj : mSweepInfo.mLeakObjects)
- {
- MarkMembers(obj);
- }
- //::MessageBoxA(NULL, "Leak", "Leak", MB_OK);
- //#if 0
- Beefy::String errorStr = StrFormat("%d object memory leak%s detected, details in Output panel.",
- mSweepInfo.mLeakCount, (mSweepInfo.mLeakCount != 1) ? "s" : "");
- gDbgErrorString = errorStr;
- gDbgErrorString += "\n";
- #ifdef BF_GC_DEBUGSWEEP
- Sleep(100);
- #endif
- for (int pass = 0; pass < 2; pass++)
- {
- int passLeakCount = 0;
- for (auto obj : mSweepInfo.mLeakObjects)
- {
- bool wantsNoRefs = pass == 0;
- bool hasNoRefs = (obj->mObjectFlags & BF_OBJECTFLAG_MARK_ID_MASK) != mCurMarkId;
- if (hasNoRefs != wantsNoRefs)
- continue;
-
- Beefy::String typeName = obj->GetTypeName();
-
- if (passLeakCount == 0)
- {
- Beefy::String header = (pass == 0) ? " Unreferenced:\n" : " Referenced by other leaked objects:\n";
- errorStr += "\x1";
- errorStr += "TEXT\t";
- errorStr += header;
- gDbgErrorString += header;
- }
- if (passLeakCount == 20000) // Only display so many...
- break;
- errorStr += "\x1";
- errorStr += StrFormat("LEAK\t(System.Object)0x%@\n", obj);
- errorStr += StrFormat(" (%s)0x%@\n", typeName.c_str(), obj);
- if (gDbgErrorString.length() < 256)
- gDbgErrorString += StrFormat(" (%s)0x%@\n", typeName.c_str(), obj);
- passLeakCount++;
- }
- }
- //TODO: Testing!
- //OutputDebugStrF(gDbgErrorString.c_str());
- gBfRtDbgCallbacks.SetErrorString(gDbgErrorString.c_str());
- gBfRtDbgCallbacks.DebugMessageData_SetupError(errorStr.c_str(), 1);
- BF_DEBUG_BREAK();
- }
- mSweepInfo.Clear();
- }
- void BFGC::ReleasePendingSpanObjects(Span* span)
- {
- if (span->freeingObjects != NULL)
- {
- Static::central_cache()[span->sizeclass].ReleasePendingSpanObjects(span);
- span->freeingObjects = NULL;
- span->freeingObjectsTail = NULL;
- }
- }
- void BFGC::ReleasePendingObjects()
- {
- BP_ZONE("ReleasePendingObjects");
-
- auto pageHeap = Static::pageheap();
- if (pageHeap == NULL)
- return;
- #ifdef BF_GC_VERIFY_SWEEP_IDS
- maxAllocNum = bf::System::Object::sCurAllocNum;
- allocIdSet.clear();
- #endif
-
- int leafCheckCount = 0;
-
- #ifdef BF32
- for (int rootIdx = 0; rootIdx < PageHeap::PageMap::ROOT_LENGTH; rootIdx++)
- {
- PageHeap::PageMap::Leaf* rootLeaf = Static::pageheap()->pagemap_.root_[rootIdx];
- if (rootLeaf == NULL)
- continue;
-
- for (int leafIdx = 0; leafIdx < PageHeap::PageMap::LEAF_LENGTH; leafIdx++)
- {
- tcmalloc_obj::Span* span = (tcmalloc_obj::Span*)rootLeaf->values[leafIdx];
- if (span != NULL)
- ReleasePendingSpanObjects(span);
- }
- }
- #else
- for (int pageIdx1 = 0; pageIdx1 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx1++)
- {
- PageHeap::PageMap::Node* node1 = Static::pageheap()->pagemap_.root_->ptrs[pageIdx1];
- if (node1 == NULL)
- continue;
- for (int pageIdx2 = 0; pageIdx2 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx2++)
- {
- PageHeap::PageMap::Node* node2 = node1->ptrs[pageIdx2];
- if (node2 == NULL)
- continue;
- for (int pageIdx3 = 0; pageIdx3 < PageHeap::PageMap::LEAF_LENGTH; pageIdx3++)
- {
- tcmalloc_obj::Span* span = (tcmalloc_obj::Span*)node2->ptrs[pageIdx3];
- if (span != NULL)
- ReleasePendingSpanObjects(span);
- }
- }
- }
- #endif
-
- #ifdef BF_GC_VERIFY_SWEEP_IDS
- for (int allocNum = 1; allocNum < maxAllocNum; allocNum++)
- {
- BF_LOGASSERT(allocIdSet.find(allocNum) != allocIdSet.end());
- }
- #endif
- }
- typedef struct _TEB {
- PVOID Reserved1[11];
- void* ThreadLocalStorage;
- void* ProcessEnvironmentBlock;
- PVOID Reserved2[399];
- BYTE Reserved3[1952];
- PVOID TlsSlots[64];
- BYTE Reserved4[8];
- PVOID Reserved5[26];
- PVOID ReservedForOle;
- PVOID Reserved6[4];
- PVOID TlsExpansionSlots;
- } TEB, *PTEB;
- typedef LONG NTSTATUS;
- typedef DWORD KPRIORITY;
- typedef WORD UWORD;
- typedef struct _CLIENT_ID
- {
- PVOID UniqueProcess;
- PVOID UniqueThread;
- } CLIENT_ID, *PCLIENT_ID;
- typedef struct _THREAD_BASIC_INFORMATION
- {
- NTSTATUS ExitStatus;
- PVOID TebBaseAddress;
- CLIENT_ID ClientId;
- KAFFINITY AffinityMask;
- KPRIORITY Priority;
- KPRIORITY BasePriority;
- } THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
- enum THREADINFOCLASS
- {
- ThreadBasicInformation,
- };
- static _TEB* GetTEB(HANDLE hThread)
- {
- bool loadedManually = false;
- HMODULE module = GetModuleHandleA("ntdll.dll");
- if (!module)
- {
- module = LoadLibraryA("ntdll.dll");
- loadedManually = true;
- }
- NTSTATUS(__stdcall *NtQueryInformationThread)(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
- NtQueryInformationThread = reinterpret_cast<decltype(NtQueryInformationThread)>(GetProcAddress(module, "NtQueryInformationThread"));
- if (NtQueryInformationThread)
- {
- NT_TIB tib = { 0 };
- THREAD_BASIC_INFORMATION tbi = { 0 };
- NTSTATUS status = NtQueryInformationThread(hThread, ThreadBasicInformation, &tbi, sizeof(tbi), nullptr);
- if (status >= 0)
- {
- _TEB* teb = (_TEB*)tbi.TebBaseAddress;
- return teb;
- }
- }
- if (loadedManually)
- {
- FreeLibrary(module);
- }
- return NULL;
- }
- static void** GetThreadLocalAddressMap(HANDLE hThread)
- {
- _TEB* teb = GetTEB(hThread);
- if (teb == NULL)
- return NULL;
- return (void**)teb->ThreadLocalStorage;
- }
- void BFGC::AdjustStackPtr(intptr& addr, int& size)
- {
- int pageSize = 4096;
- // There's a race condition where RSP can be adjusted into a guard page region before the
- // guard page is actually removed. The guard page is a fire-once error so we can't just
- // ignore it or else we rob that read of it's ability to expand (crash)
- while (size > 0)
- {
- MEMORY_BASIC_INFORMATION memoryInfo;
- int returnSize = ::VirtualQuery((void*)addr, &memoryInfo, sizeof(memoryInfo));
- if ((returnSize > 0) && ((memoryInfo.Protect & (PAGE_GUARD | PAGE_NOACCESS)) == 0))
- return;
-
- addr += pageSize;
- size -= pageSize;
- }
- }
- bool BFGC::ScanThreads()
- {
- BP_ZONE("BFGC::ScanThreads");
- mUsingThreadUnlocked = true;
- BF_FULL_MEMORY_FENCE();
- //BP_ZONE("ScanThreads");
-
- bool didWork = false;
-
- mStackScanIdx++;
- mDoStackDeepMark = true;
-
- int threadIdx = 0;
- while (true)
- {
- ThreadInfo* thread = NULL;
- {
- AutoCrit autoCrit(mCritSect);
- if (threadIdx >= mThreadList.size())
- break;
- thread = mThreadList[threadIdx++];
- }
- if (!thread->mRunning)
- {
- AutoCrit autoCrit(mCritSect);
- BF_ASSERT(mThreadList[threadIdx - 1] == thread);
- delete thread;
- mThreadList.RemoveAt(threadIdx - 1);
- continue;
- }
- BP_ZONE("ThreadCollect");
- //Beefy::DebugTimeGuard suspendTimeGuard(10, "ThreadSuspend");
- DWORD result = 0;
-
- //BFMark(thread);
- //MarkObject(thread->mThread);
- // If (thread->mLastGCScanIdx == mCurScanIdx), that means this is the second cycle of running through here,
- // which could happen if we added another thread while scanning so we need another pass tSuspendThreadhrough to
- // catch the new one
- if (((mGCThread != NULL) && (thread->mThreadHandle == mGCThread)) ||
- (!thread->mRunning) /*|| (thread->mLastGCScanIdx == mCurScanIdx)*/)
- {
- continue;
- }
- #ifdef BF_GC_LOG_ENABLED
- // If we're writing the log from another thread then wait for it -- we'll deadlock if we pause that thread and then try to log
- while (gGCLog.mWriting)
- {
- Sleep(20);
- }
- #endif
-
- //printf("Processing Thread:%p Handle:%p\n", thread, thread->mThreadHandle);
- //suspendTimeGuard.Start();
- //DWORD lastError = GetLastError();
- BF_LOGASSERT(result == 0);
-
- didWork = true;
- BFLOG2(GCLog::EVENT_SCAN_THREAD, (intptr)thread, (intptr)thread->mThreadId);
- for (auto obj : thread->mStackMarkableObjects)
- {
- MarkMembers(obj);
- }
- intptr regVals[64];
- intptr stackPtr = 0;
- BfpThreadResult threadResult;
- int regValCount = 64;
- BfpThread_GetIntRegisters(thread->mThreadHandle, &stackPtr, regVals, ®ValCount, &threadResult);
- BF_ASSERT(threadResult == BfpThreadResult_Ok);
-
- void** threadLoadAddressMap = (void**)((_TEB*)thread->mTEB)->ThreadLocalStorage;
- for (auto& tlsMember : mTLSMembers)
- {
- void* threadLoadAddress = threadLoadAddressMap[tlsMember.mTLSIndex];
- typedef void(*MarkFunc)(void*);
- MarkFunc markFunc = *(MarkFunc*)&tlsMember.mMarkFunc;
- markFunc((uint8*)threadLoadAddress + tlsMember.mTLSOffset);
- }
-
- mQueueMarkObjects = true;
- ConservativeScan(regVals, regValCount * sizeof(intptr));
- int length = thread->mStackStart - stackPtr;
-
- AdjustStackPtr(stackPtr, length);
- ConservativeScan((void*)stackPtr, length);
- mQueueMarkObjects = false;
- if (mDoStackDeepMark)
- {
- HandlePendingGCData();
- }
- //suspendTimeGuard.Stop();
- BF_LOGASSERT(result != -1);
- if (!mPendingGCData.IsEmpty())
- {
- BP_ZONE("HandlePendingGCData(Thread)");
- HandlePendingGCData();
- }
- }
- // This can be write barrier objects be from dead threads
- HandlePendingGCData();
- BF_FULL_MEMORY_FENCE();
- mUsingThreadUnlocked = false;
- return didWork;
- }
- int gAddedTypeCount = 0;
- int gGCTypeCounts = 0;
- int gGCAssemblyCount = 0;
- static void GCObjFree(void* ptr)
- {
- Span* span = TCGetSpanAt(ptr);
- if (span == NULL)
- {
- BF_DBG_FATAL("Bad");
- tc_free(ptr);
- return;
- }
- if (span->sizeclass == 0)
- {
- // Clear out all memory
- intptr pageSize = (intptr)1 << kPageShift;
- int spanSize = pageSize * span->length;
- void* spanStart = (void*)((intptr)span->start << kPageShift);
- memset(spanStart, 0, spanSize);
- tc_free(ptr);
- return;
- }
-
-
- int size = Static::sizemap()->class_to_size(span->sizeclass);
- int dataOffset = (int)(sizeof(intptr) * 2);
- memset((uint8*)ptr + dataOffset, 0, size - dataOffset);
- // const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
- // size_t cl = Static::pageheap()->GetSizeClassIfCached(p);
- // if (cl == 0)
- // {
- // tc_free(ptr);
- // return;
- // }
- //
- // Span* span = TCGetSpanAt(ptr);
- if (span->freeingObjectsTail == NULL)
- {
- span->freeingObjectsTail = ptr;
- *((intptr*)span->freeingObjectsTail) = 1;
- }
- else
- {
- // Increment pending size
- (*((intptr*)span->freeingObjectsTail))++;
- *(reinterpret_cast<void**>(ptr)) = span->freeingObjects;
- }
-
- span->freeingObjects = ptr;
- }
- void BFGC::DoCollect(bool doingFullGC)
- {
- BP_ZONE("Collect");
- mThreadId = BfpThread_GetCurrentId();
- mStage = 0;
- gTargetFoundCount = 0;
- gMarkTargetCount = 0;
- gMarkTargetMutatorCount = 0;
- mCurGCMarkCount = 0;
- mCurMutatorMarkCount = 0;
- mCurGCObjectQueuedCount = 0;
- mCurMutatorObjectQueuedCount = 0;
- mCurObjectDeleteCount = 0;
- mCurFinalizersCalled = 0;
- mCurSweepFoundCount = 0;
- mCurSweepFoundPermanentCount = 0;
- mCurFreedBytes = 0;
- if (doingFullGC)
- {
- int nextMark = sCurMarkId + 1;
- if (nextMark == 4)
- nextMark = 1;
- mCurMarkId = nextMark;
- sCurMarkId = nextMark;
- sAllocFlags = sCurMarkId;
- }
- mStage = 1;
- BFLOG2(GCLog::EVENT_GC_START, sCurMarkId, BfpSystem_TickCount());
- gGCTypeCounts = 0;
- if (!mSkipMark)
- {
- BP_ZONE("MarkStatics");
- MarkStatics();
- RawMarkAll();
- bool success = bf::System::GC::DoCallRootCallbacks();
- if ((!success) && (!mHadRootError))
- {
- OutputDebugStringA("WARNING: GC leak detection disabled, a GC root callback disabled it\n");
- mHadRootError = true;
- }
- }
-
- if (!mSkipMark)
- {
- Beefy::AutoCrit autoCrit(mCritSect);
- BP_ZONE("ExplicitRootsMark");
- mQueueMarkObjects = true;
- for (int i = 0; i < (int)mExplicitRoots.size(); i++)
- MarkObject(mExplicitRoots[i]);
- mQueueMarkObjects = false;
- }
- if ((doingFullGC) && (!mSkipMark))
- {
- //MarkObject(mGCThread);
- BFMarkThreadPoolJobs();
- BFMarkCOMObjects();
- }
- int threadIdx = 0;
-
- // We need to turn roots black, otherwise the mutator could move a member
- // of a gray root to the stack after the stack scan and we'd miss it
- {
- BP_ZONE("Collect - HandlePendingGCData(Roots)");
- HandlePendingGCData();
- }
- mStage = 2;
- mCurScanIdx++;
- int passes = 0;
-
- if (doingFullGC)
- {
- if (!mSkipMark)
- {
- ScanThreads();
- HandlePendingGCData();
- }
- }
- BF_ASSERT(mPendingGCData.IsEmpty());
- }
- void BFGC::FinishCollect()
- {
- //OutputDebugStrF("Collected %d objects\n", mFinalizeList.size());
- if ((gBfRtDbgFlags & BfRtFlags_ObjectHasDebugFlags) == 0)
- return;
- mLastFreeCount = 0;
- mStage = 3;
-
- typedef std::unordered_set<bf::System::Object*> BfObjectSet;
-
- {
-
- // Handle any pending data from strong GCHandle references
- HandlePendingGCData();
- }
- int finalizeDataCountFound = 0;
-
- if (mDebugDumpState == DEBUGDUMPSTATE_WAITING_FOR_GC)
- {
- WriteDebugDumpState();
- mDebugDumpState = DEBUGDUMPSTATE_NONE;
- }
-
- {
- BP_ZONE("FreeingObjects");
- void* lastPtr = NULL;
- for (int i = 0; i < mFinalizeList.size(); i++)
- {
- bf::System::Object* obj = mFinalizeList[i];
- if (obj == NULL)
- continue;
- BF_LOGASSERT((obj->mObjectFlags & (/*BF_OBJECTFLAG_FREED |*/ BF_OBJECTFLAG_ALLOCATED)) == BF_OBJECTFLAG_ALLOCATED);
- //BF_LOGASSERT(obj > lastPtr);
- lastPtr = obj;
- }
- {
- BP_ZONE("ReleaseAtLeastNPages");
- SpinLockHolder h(tcmalloc_obj::Static::pageheap_lock());
- Static::pageheap()->ReleaseAtLeastNPages(256);
- }
- {
- BP_ZONE("DecommitFromReleasedList");
- Static::pageheap()->DecommitFromReleasedList(mForceDecommit);
- mForceDecommit = false;
- }
- Dictionary<bf::System::Type*, int> sizeMap;
- int objFreeSize = 0;
- for (int i = 0; i < mFinalizeList.size(); i++)
- {
- bf::System::Object* obj = mFinalizeList[i];
- if (obj == NULL)
- continue;
- // Removed from list already?
- if ((obj->mObjectFlags & BF_OBJECTFLAG_MARK_ID_MASK) == mCurMarkId)
- continue;
- #ifdef BF_DEBUG
- if ((obj->mObjectFlags & (/*BF_OBJECTFLAG_FREED |*/ BF_OBJECTFLAG_ALLOCATED)) != BF_OBJECTFLAG_ALLOCATED)
- {
- tcmalloc_obj::Span* span = TCGetSpanAt(obj);
- BF_FATAL("Object corrupted");
- }
- #ifdef TARGET_TYPE
- if (obj->mBFVData->mType == TARGET_TYPE)
- {
- printf("Finalizing target %p\n", obj);
- }
- #endif
- #endif
- BFLOG1(GCLog::EVENT_FREE, (intptr)obj);
- // BYE!
- #ifdef BF_NO_FREE_MEMORY
- //obj->mObjectFlags |= BF_OBJECTFLAG_FREED;
- #else
-
- #ifdef BG_GC_TRACKPTRS
- {
- Beefy::AutoCrit autoCrit(gBFGC.mCritSect);
- gTrackPtr.erase(gTrackPtr.find(obj));
- }
- #endif
- int objSize = BFGetObjectSize(obj);
- objFreeSize += objSize;
- if (mDisplayFreedObjects)
- {
- // Temporarily remove object flags so GetType() won't fail
- obj->mObjectFlags = BfObjectFlag_None;
- bf::System::Type* type = obj->_GetType();
- //auto pairVal = sizeMap.insert(std::make_pair(type, 0));
- //int newSize = pairVal.first->second + objSize;
- int* sizePtr = NULL;
- sizeMap.TryAdd(type, NULL, &sizePtr);
- *sizePtr += objSize;
- //pairVal.first->second = newSize;
- }
- //obj->mObjectFlags = BfObjectFlag_None;
- obj->mAllocCheckPtr = 0;
- mLastFreeCount++;
- #ifdef BF_GC_USE_OLD_FREE
- tc_free(obj);
- #else
- GCObjFree(obj);
- #endif
-
- #ifdef BF_GC_PRINTSTATS
- ::InterlockedIncrement((volatile uint32*) &gBFGC.mTotalFrees);
- #endif
-
- #endif
- mCurObjectDeleteCount++;
- }
-
- if (!sizeMap.IsEmpty())
- {
- std::multimap<int, bf::System::Type*> orderedSizeMap;
- int totalSize = 0;
- for (auto& pair : sizeMap)
- {
- totalSize += pair.mValue;
- orderedSizeMap.insert(std::make_pair(-pair.mValue, pair.mKey));
- }
- Beefy::String msg;
- msg += Beefy::StrFormat("GC Live Count : %d\n", mCurLiveObjectCount);
- msg += Beefy::StrFormat("GC Freed Count : %d\n", mLastFreeCount);
- msg += Beefy::StrFormat("GC Freed Size : %dk\n", (int)(objFreeSize / 1024));
- msg += "GC Objects\n";
- for (auto& pair : orderedSizeMap)
- {
- bf::System::Type* typeName = pair.second;
- msg += StrFormat(" %-37s : %dk\n", typeName->GetFullName().c_str(), (-pair.first + 1023) / 1024);
- }
- Beefy::OutputDebugStr(msg.c_str());
- }
- mBytesFreed += objFreeSize;
- mBytesRequested -= objFreeSize;
- mCurFreedBytes += objFreeSize;
- }
- #ifdef BF_GC_USE_OLD_FREE
- {
- BP_ZONE("TCScavenge");
- tcmalloc_obj::ThreadCache::GetCacheWhichMustBePresent()->ForceScavenge();
- }
- #else
- {
- //Beefy::DebugTimeGuard suspendTimeGuard(10, "BFGC::ReleasePendingObjects");
- ReleasePendingObjects();
- }
- #endif
- mFinalizeList.Clear();
- mStage = 4;
- }
- void BFGC::Run()
- {
- BfpThread_SetName(BfpThread_GetCurrent(), "BFGC", NULL);
- uint32 lastGCTick = BFTickCount();
- while (!mExiting)
- {
- float fullGCPeriod = mFullGCPeriod;
- if ((fullGCPeriod != -1) && (mMaxPausePercentage > 0) && (!mCollectReports.IsEmpty()))
- {
- // When we are debugging, we can have a very long update when stepping through code,
- // but otherwise try to pick an update period that keeps our pause time down
- float maxExpandPeriod = BF_MAX(mFullGCPeriod, 2000);
- auto& collectReport = mCollectReports.back();
- fullGCPeriod = BF_MAX(fullGCPeriod, collectReport.mPausedMS * 100 / mMaxPausePercentage);
- fullGCPeriod = BF_MIN(fullGCPeriod, maxExpandPeriod);
- }
- int waitPeriod = fullGCPeriod;
- if (waitPeriod == 0)
- waitPeriod = -1;
- mCollectEvent.WaitFor(waitPeriod);
-
- uint32 tickNow = BFTickCount();
- if ((fullGCPeriod >= 0) && (tickNow - lastGCTick >= fullGCPeriod))
- mCollectRequested = true;
- if ((mFreeTrigger >= 0) && (mFreeSinceLastGC >= mFreeTrigger))
- mCollectRequested = true;
- if (!mCollectRequested)
- continue;
- lastGCTick = tickNow;
- mCollectRequested = false;
- mPerformingCollection = true;
- BF_FULL_MEMORY_FENCE();
- PerformCollection();
- BF_FULL_MEMORY_FENCE();
- mPerformingCollection = false;
- BF_FULL_MEMORY_FENCE();
- mCollectDoneEvent.Set(true);
- }
-
- mRunning = false;
- }
- void BFGC::RunStub(void* gc)
- {
- ((BFGC*)gc)->Run();
- }
- void BFGC::ThreadStarted(BfDbgInternalThread* thread)
- {
- Beefy::AutoCrit autoCrit(mCritSect);
-
- //thread->mTCMallocObjThreadCache = tcmalloc_obj::ThreadCache::GetCache();
- //mThreadList.push_back(thread);
- BFLOG2(GCLog::EVENT_THREAD_STARTED, (intptr)thread, (intptr)BfpThread_GetCurrentId());
- //printf("ThreadStarted: %p intern:%p TID:%d\n", thread->mThread, thread, ::GetCurrentThreadId());
- }
- void BFGC::ThreadStopped(BfDbgInternalThread* thread)
- {
- Beefy::AutoCrit autoCrit(mCritSect);
- // Keep sync to avoid having the thread exit while the GC is trying to pause it,
- // but do the actual cleanup from the GC's thread
- //thread->mDone = true;
- //printf("ThreadStopped: %p intern:%p TID:%d\n", thread->mThread, thread, ::GetCurrentThreadId());
- }
- void BFGC::ThreadStarted()
- {
- Beefy::AutoCrit autoCrit(mCritSect);
- ThreadInfo* thread = new ThreadInfo();
- thread->mRunning = true;
- thread->mThreadHandle = BfpThread_GetCurrent();
- thread->mThreadId = BfpThread_GetCurrentId();
- thread->mTEB = GetTEB((HANDLE)thread->mThreadHandle);
-
- intptr stackBase;
- int stackLimit;
- BfpThread_GetStackInfo(thread->mThreadHandle, &stackBase, &stackLimit, NULL);
- thread->mStackStart = stackBase;
- mThreadList.Add(thread);
- ThreadInfo::sCurThreadInfo = thread;
- }
- void BFGC::ThreadStopped()
- {
- auto thread = ThreadInfo::sCurThreadInfo;
- if (thread != NULL)
- {
- Beefy::AutoCrit autoCrit(mCritSect);
- BF_ASSERT(thread->mStackMarkableObjects.IsEmpty());
- if (!mUsingThreadUnlocked)
- {
- // Just delete it
- mThreadList.Remove(thread);
- delete thread;
- }
- else
- {
- thread->mRunning = false;
- }
- }
- }
- void BFGC::Init()
- {
- //ThreadStarted();
- Start();
- }
- void BFGC::Start()
- {
- #ifndef BF_GC_DISABLED
- //mEphemeronTombstone = Object::BFCreate();
- //RegisterRoot(mEphemeronTombstone);
- mRunning = true;
- #ifdef BF_DEBUG
- // More stack space is needed in debug version
- //::CreateThread(NULL, 64*1024, (LPTHREAD_START_ROUTINE)&RunStub, (void*)this, 0, (DWORD*)&mThreadId);
- mGCThread = BfpThread_Create(RunStub, (void*)this, 256*1024, (BfpThreadCreateFlags)(BfpThreadCreateFlag_Suspended | BfpThreadCreateFlag_StackSizeReserve), &mThreadId);
- #else
- mGCThread = BfpThread_Create(RunStub, (void*)this, 64 * 1024, (BfpThreadCreateFlags)(BfpThreadCreateFlag_Suspended | BfpThreadCreateFlag_StackSizeReserve), &mThreadId);
- #endif
-
- BfpThread_Resume(mGCThread, NULL);
- #endif
- }
- void BFGC::StopCollecting()
- {
- if (!mRunning)
- return;
- mExiting = true;
- while (mRunning)
- {
- if (BfpThread_WaitFor(mGCThread, 0))
- {
- OutputDebugStr("BeefDbgRT not shut down gracefully!\n");
- mGracelessShutdown = true;
- mRunning = false;
- break;
- }
- //BFRtLock bfLock(mEphemeronTombstone);
- mWaitingForGC = true;
- // Wait for current collection to finish
- mCollectEvent.Set();
- //Monitor::Monitor_wait(mEphemeronTombstone, 20);
- mWaitingForGC = false;
- }
- }
- void BFGC::AddStackMarkableObject(bf::System::Object* obj)
- {
- auto threadInfo = ThreadInfo::sCurThreadInfo;
- Beefy::AutoCrit autoCrit(threadInfo->mCritSect);
- threadInfo->mStackMarkableObjects.Add(obj);
- }
- void BFGC::RemoveStackMarkableObject(bf::System::Object* obj)
- {
- auto threadInfo = ThreadInfo::sCurThreadInfo;
- Beefy::AutoCrit autoCrit(threadInfo->mCritSect);
- int stackIdx = threadInfo->mStackMarkableObjects.LastIndexOf(obj);
- BF_ASSERT(stackIdx != -1);
- if (stackIdx != -1)
- threadInfo->mStackMarkableObjects.RemoveAtFast(stackIdx);
- }
- void BFGC::Shutdown()
- {
- if (mShutdown)
- return;
- mShutdown = true;
- StopCollecting();
- Beefy::AutoCrit autoCrit(mCritSect);
- if (mGracelessShutdown)
- return;
- // Report any objects that aren't deleted
- mSweepInfo.mShowAllAsLeaks = true;
- Sweep();
- ProcessSweepInfo();
- RawShutdown();
- TCMalloc_FreeAllocs();
-
- mFinalizeList.Dispose();
- mPendingGCData.Dispose();
- for (auto thread : mThreadList)
- thread->mStackMarkableObjects.Dispose();
- }
- void BFGC::InitDebugDump()
- {
- mDebugDumpState = DEBUGDUMPSTATE_WAITING_FOR_PREV;
- mCollectEvent.Set();
- while (mDebugDumpState < DEBUGDUMPSTATE_WAITING_FOR_MUTATOR)
- {
- //BFRtLock bfLock(mEphemeronTombstone);
- //Monitor::Monitor_wait(mEphemeronTombstone, 20);
- }
- }
- void BFGC::EndDebugDump()
- {
- mDebugDumpState = DEBUGDUMPSTATE_WAITING_FOR_GC;
- }
- intptr gFindAddrVal = 0;
- void BFGC::DebugDumpLeaks()
- {
- CheckTcIntegrity();
- BP_ZONE("DebugDump");
- if (mExiting)
- return;
- mSkipMark = true;
- Collect(false);
- }
- void BFGC::ObjReportHandleSpan(tcmalloc_obj::Span* span, int expectedStartPage, int& objectCount, intptr& freeSize, Beefy::Dictionary<bf::System::Type*, AllocInfo>& sizeMap)
- {
- if (span->location != tcmalloc_obj::Span::IN_USE)
- return;
- if (span->start != expectedStartPage)
- {
- return;
- }
- intptr pageSize = (intptr)1<<kPageShift;
- int spanSize = pageSize * span->length;
- void* spanStart = (void*)((intptr)span->start << kPageShift);
- void* spanEnd = (void*)((intptr)spanStart + spanSize);
- void* spanPtr = spanStart;
- BF_LOGASSERT((spanStart >= tcmalloc_obj::PageHeap::sAddressStart) && (spanEnd <= tcmalloc_obj::PageHeap::sAddressEnd));
- int elementSize = Static::sizemap()->ByteSizeForClass(span->sizeclass);
- if (elementSize == 0)
- elementSize = spanSize;
- BF_LOGASSERT(elementSize >= sizeof(bf::System::Object));
- while (spanPtr <= (uint8*)spanEnd - elementSize)
- {
- bf::System::Object* obj = (bf::System::Object*)spanPtr;
- if (obj->mAllocCheckPtr != 0)
- {
- int objectFlags = obj->mObjectFlags;
- if ((objectFlags & BF_OBJECTFLAG_DELETED) == 0)
- {
- bf::System::Type* type = obj->_GetType();
- //auto pairVal = sizeMap.insert(std::make_pair(type, 0));
- //int newSize = pairVal.first->second + elementSize;
- //pairVal.first->second = newSize;
- AllocInfo* sizePtr = NULL;
- sizeMap.TryAdd(type, NULL, &sizePtr);
- sizePtr->mCount++;
- sizePtr->mSize += elementSize;
- objectCount++;
- }
- }
- else
- freeSize += elementSize;
- spanPtr = (void*)((intptr)spanPtr + elementSize);
- }
- }
- void BFGC::ObjReportScan(int& objectCount, intptr& freeSize, Beefy::Dictionary<bf::System::Type*, AllocInfo>& sizeMap)
- {
- auto pageHeap = Static::pageheap();
- if (pageHeap == NULL)
- return;
- #ifdef BF32
- int checkPageId = (int)((uintptr)gFindAddrVal >> kPageShift);
- int checkRootIdx = checkPageId >> PageHeap::PageMap::LEAF_BITS;
- int checkLeafIdx = checkPageId & (PageHeap::PageMap::LEAF_LENGTH - 1);
- for (int rootIdx = 0; rootIdx < PageHeap::PageMap::ROOT_LENGTH; rootIdx++)
- {
- PageHeap::PageMap::Leaf* rootLeaf = Static::pageheap()->pagemap_.root_[rootIdx];
- if (rootLeaf == NULL)
- continue;
- for (int leafIdx = 0; leafIdx < PageHeap::PageMap::LEAF_LENGTH; leafIdx++)
- {
- tcmalloc_obj::Span* span = (tcmalloc_obj::Span*)rootLeaf->values[leafIdx];
- if (span != NULL)
- {
- int expectedStartPage = (rootIdx * PageHeap::PageMap::LEAF_LENGTH) + leafIdx;
- ObjReportHandleSpan(span, expectedStartPage, objectCount, freeSize, sizeMap);
- // We may be tempted to advance by span->length here, BUT
- // let us just scan all leafs becuause span data is
- // sometimes invalid and a long invalid span can cause
- // us to skip over an actual valid span
- }
- }
- }
- #else
- for (int pageIdx1 = 0; pageIdx1 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx1++)
- {
- PageHeap::PageMap::Node* node1 = Static::pageheap()->pagemap_.root_->ptrs[pageIdx1];
- if (node1 == NULL)
- continue;
- for (int pageIdx2 = 0; pageIdx2 < PageHeap::PageMap::INTERIOR_LENGTH; pageIdx2++)
- {
- PageHeap::PageMap::Node* node2 = node1->ptrs[pageIdx2];
- if (node2 == NULL)
- continue;
- for (int pageIdx3 = 0; pageIdx3 < PageHeap::PageMap::LEAF_LENGTH; pageIdx3++)
- {
- tcmalloc_obj::Span* span = (tcmalloc_obj::Span*)node2->ptrs[pageIdx3];
- if (span != NULL)
- {
- int expectedStartPage = ((pageIdx1 * PageHeap::PageMap::INTERIOR_LENGTH) + pageIdx2) * PageHeap::PageMap::LEAF_LENGTH + pageIdx3;
- ObjReportHandleSpan(span, expectedStartPage, objectCount, freeSize, sizeMap);
- // We may be tempted to advance by span->length here, BUT
- // let us just scan all leafs because span data is
- // sometimes invalid and a long invalid span can cause
- // us to skip over an actual valid span
- }
- }
- }
- }
- #endif
- }
- void BFGC::Report()
- {
- AutoCrit autoCrit(mCritSect);
- CheckTcIntegrity();
- BP_ZONE("Report");
-
- Beefy::String msg;
- int objectCount = 0;
- #ifdef BF_GC_VERIFY_SWEEP_IDS
- maxAllocNum = bf::System::Object::sCurAllocNum;
- allocIdSet.clear();
- #endif
- int leafCheckCount = 0;
- bool overflowed = false;
- Dictionary<bf::System::Type*, AllocInfo> sizeMap;
- intptr objFreeSize = 0;
- ObjReportScan(objectCount, objFreeSize, sizeMap);
- std::multimap<AllocInfo, bf::System::Type*> orderedSizeMap;
- intptr totalSize = 0;
- for (auto& pair : sizeMap)
- {
- orderedSizeMap.insert(std::make_pair(pair.mValue, pair.mKey));
- }
- msg += "Overall GC Summary\n";
- //msg += Beefy::StrFormat(" TotalAllocs %d\n", mTotalAllocs);
- //msg += Beefy::StrFormat(" TotalAllocs - TotalFrees %d\n", mTotalAllocs - mTotalFrees);
- msg += Beefy::StrFormat(" System Memory Taken %dk\n", (int)(TCMalloc_SystemTaken / 1024));
- //msg += Beefy::StrFormat(" BytesRequested %dk\n", (int)(mBytesRequested / 1024));
- msg += Beefy::StrFormat(" Live Objects %d\n", objectCount);
- msg += Beefy::StrFormat(" Last Object Freed Count %d\n", mLastFreeCount);
-
- intptr reportedCount = 0;
- intptr rawFreeSize = 0;
- gBFGC.RawReport(msg, rawFreeSize, orderedSizeMap);
- for (auto& pair : orderedSizeMap)
- {
- totalSize += pair.first.mSize;
- reportedCount += pair.first.mCount;
- }
- msg += Beefy::StrFormat(" Scanned Alloc Count %d\n", (int)(reportedCount));
- msg += Beefy::StrFormat(" Used Memory %dk\n", (int)(totalSize / 1024));
- msg += Beefy::StrFormat(" Object Unusued Memory %dk\n", (int)(objFreeSize / 1024));
- msg += Beefy::StrFormat(" Raw Unusued Memory %dk\n", (int)(rawFreeSize / 1024));
-
- if (!mCollectReports.IsEmpty())
- {
- for (int reportIdx = 0; reportIdx < (int)mCollectReports.size(); reportIdx++)
- {
- auto& report = mCollectReports[reportIdx];
- msg += Beefy::StrFormat(" Collection %d Total: %dms Paused: %dms CollectCount: %d", report.mCollectIdx, report.mTotalMS, report.mPausedMS, report.mCollectCount);
- if (reportIdx > 0)
- {
- msg += Beefy::StrFormat(" SinceLast: %dms", report.mStartTick - mCollectReports[reportIdx - 1].mStartTick);
- }
- msg += "\n";
- }
-
- msg += Beefy::StrFormat(" Average Time Between Collections %dms\n", BFTickCount() / mCollectIdx);
- }
-
- msg += "Types Size Count\n";
- for (auto& pair : orderedSizeMap)
- {
- bf::System::Type* type = pair.second;
- Beefy::String typeName;
- if (type == NULL)
- typeName = "NULL";
- else
- typeName = type->GetFullName();
-
- msg += StrFormat(" %-62s %7dk %7d\n", typeName.c_str(), (pair.first.mSize + 1023) / 1024, pair.first.mCount);
- }
- Beefy::OutputDebugStr(msg.c_str());
- BFGCLogWrite();
- }
- void BFGC::ReportTLSMember(int tlsIndex, void* ptr, void* markFunc)
- {
- if (mMainThreadTLSPtr == NULL)
- {
- _TEB* teb = NtCurrentTeb();
- mMainThreadTLSPtr = teb->ThreadLocalStorage;
- }
- TLSMember tlsMember;
- tlsMember.mTLSOffset = (uint8*)ptr - ((uint8**)mMainThreadTLSPtr)[tlsIndex];
- tlsMember.mMarkFunc = markFunc;
- tlsMember.mTLSIndex = tlsIndex;
- mTLSMembers.Add(tlsMember);
- }
- void BFGC::SuspendThreads()
- {
- BP_ZONE("TriggerCollection - SuspendThreads");
- auto curThreadId = GetCurrentThreadId();
- for (auto thread : mThreadList)
- {
- if ((thread->mThreadId != curThreadId) && (thread->mRunning))
- {
- // We must lock this before suspending so we can access mStackMarkableObjects
- // Otherwise we could deadlock
- thread->mCritSect.Lock();
- BfpThreadResult result;
- BfpThread_Suspend(thread->mThreadHandle, &result);
- ASSERT(result == BfpThreadResult_Ok);
- }
- }
- }
- void BFGC::ResumeThreads()
- {
- BP_ZONE("TriggerCollection - ResumeThreads");
- auto curThreadId = GetCurrentThreadId();
- for (auto thread : mThreadList)
- {
- if ((thread->mThreadId != curThreadId) && (thread->mRunning))
- {
- // Previously locked in SuspendThreads
- thread->mCritSect.Unlock();
- BfpThread_Resume(thread->mThreadHandle, NULL);
- }
- }
- }
- void BFGC::PerformCollection()
- {
- BP_ZONE("TriggerCollection");
-
- DWORD startTick = BFTickCount();
- CollectReport collectReport;
- collectReport.mCollectIdx = mCollectIdx;
- collectReport.mStartTick = startTick;
- #ifndef BF_MINGW
- //_CrtCheckMemory();
- #endif
- #ifdef BF_GC_INCREMENTAL
- mFullGCTriggered = true;
- mForceDecommit |= forceDecommit;
- gBFGC.mCollectEvent.Set();
- #else
- Beefy::AutoCrit autoCrit(mCritSect);
- mAllocSinceLastGC = 0;
- // This was old "emergency" debugging code to make sure we weren't doing a malloc in the GC code,
- // but it's a multi-threaded race condition
- /*uint8* mallocAddr = (uint8*)&malloc;
- DWORD oldProtect = 0;
- BOOL worked = ::VirtualProtect(mallocAddr, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
- uint8 oldCode = *mallocAddr;
- *mallocAddr = 0xCC;*/
- mPendingGCData.Reserve(BF_GC_MAX_PENDING_OBJECT_COUNT);
- uint32 suspendStartTick = BFTickCount();
- SuspendThreads();
- #ifndef BF_MINGW
- //_CrtCheckMemory();
- #endif
- DoCollect(true);
- #ifndef BF_MINGW
- //_CrtCheckMemory();
- #endif
- //*mallocAddr = oldCode;
-
- #ifdef BF_GC_EMPTYSCAN
- {
- mSweepInfo.mEmptyScan = true;
- Sweep();
- mSweepInfo.mEmptyScan = false;
- }
- #endif
- mFreeSinceLastGC = 0;
- BFLOG2(GCLog::EVENT_GC_UNFREEZE, sCurMarkId, BfpSystem_TickCount());
- #ifndef BF_GC_DEBUGSWEEP
- ResumeThreads();
- #endif
- collectReport.mPausedMS = BFTickCount() - suspendStartTick;
-
- //BFGCLogWrite();
- mFinalizeList.Clear();
- Sweep();
- #ifdef BF_GC_DEBUGSWEEP
- ResumeThreads();
- #endif
- collectReport.mCollectCount = (int)mFinalizeList.size();
- FinishCollect();
- ReleasePendingObjects();
- ProcessSweepInfo();
- BFLOG2(GCLog::EVENT_GC_DONE, sCurMarkId, BfpSystem_TickCount());
- collectReport.mTotalMS = BFTickCount() - startTick;
- // while (mCollectReports.size() > 4)
- // mCollectReports.RemoveAt(0);
- while (mCollectReports.size() > 10)
- mCollectReports.RemoveAt(0);
- mCollectReports.Add(collectReport);
- mCollectIdx++;
- #endif
- }
- void BFGC::Collect(bool async)
- {
- mCollectRequested = true;
- BF_FULL_MEMORY_FENCE();
- if (async)
- {
- mCollectEvent.Set();
- }
- else
- {
- if (mPerformingCollection)
- mCollectDoneEvent.WaitFor(0); // Wait for previous to finish
- mCollectDoneEvent.Reset();
- mCollectEvent.Set();
- mCollectDoneEvent.WaitFor();
- }
- }
- void BFGC::WriteDebugDumpState()
- {
- struct DebugInfo
- {
- bf::System::Type* mType;
- int mCount;
- int mSize;
- int mAllocSize;
- DebugInfo()
- {
- mType = NULL;
- mCount = 0;
- mSize = 0;
- mAllocSize = 0;
- }
- };
- std::vector<DebugInfo> debugInfoVector;
- for (int i = 0; i < (int)mFinalizeList.size(); i++)
- {
- bf::System::Object* obj = (bf::System::Object*) mFinalizeList[i];
- if ((bf::System::Type*)obj->GetTypeSafe() != NULL)
- {
- if ((uintptr)obj->GetTypeSafe() <= 1024U*1024U)
- {
- while ((int) debugInfoVector.size() <= 0)
- debugInfoVector.push_back(DebugInfo());
- DebugInfo* debugInfo = &debugInfoVector[0];
- debugInfo->mType = NULL;
- debugInfo->mCount++;
- int objSize = BFGetObjectSize(obj);
- debugInfo->mSize += objSize;
- debugInfo->mAllocSize += objSize;
- //debugInfo->mAllocSize += MallocExtension::instance()->GetEstimatedAllocatedSize(objSize);
- }
- else
- {
- //const bf::System::Type* bfTypeRootData = ((bf::System::Type*)obj->GetTypeSafe())->mTypeRootData;
- bf::System::Type* bfType = obj->GetTypeSafe();
- while ((int) debugInfoVector.size() <= bfType->mTypeId)
- debugInfoVector.push_back(DebugInfo());
- DebugInfo* debugInfo = &debugInfoVector[bfType->mTypeId];
- debugInfo->mType = obj->GetTypeSafe();
- debugInfo->mCount++;
- int objSize = BFGetObjectSize(obj);
- debugInfo->mSize += objSize;
- debugInfo->mAllocSize += objSize;
- //debugInfo->mAllocSize += MallocExtension::instance()->GetEstimatedAllocatedSize(objSize);
- }
- }
- }
- typedef std::multimap<int, DebugInfo*> DebugInfoMap;
- DebugInfoMap debugInfoMap;
- for (int i = 0; i < (int) debugInfoVector.size(); i++)
- {
- DebugInfo* debugInfo = &debugInfoVector[i];
- if (debugInfo->mCount > 0)
- debugInfoMap.insert(DebugInfoMap::value_type(-debugInfo->mSize, debugInfo));
- }
- Beefy::String dbgStr = "\n\nBeefyRT GC DebugDump:\n";
- int countTotal = 0;
- int sizeTotal = 0;
- int allocSizeTotal = 0;
- DebugInfoMap::iterator itr = debugInfoMap.begin();
- while (itr != debugInfoMap.end())
- {
- DebugInfo* debugInfo = itr->second;
- Beefy::String lineStr = StrFormat("%8d %8dk %8dk ", debugInfo->mCount, (debugInfo->mSize + 1023) / 1024, (debugInfo->mAllocSize + 1023) / 1024);
- Beefy::String typeName;
- if (debugInfo->mType != NULL)
- typeName = debugInfo->mType->GetFullName();
- else
- typeName = "???";
-
- lineStr += typeName;
- dbgStr += lineStr += "\n";
- countTotal += debugInfo->mCount;
- sizeTotal += debugInfo->mSize;
- allocSizeTotal += debugInfo->mAllocSize;
- ++itr;
- }
- dbgStr += StrFormat("%8d %8dk %8dk TOTAL", countTotal, (sizeTotal + 1023)/1024, (allocSizeTotal + 1023)/1024);
- OutputDebugStrF(dbgStr.c_str());
- }
- #ifdef BF_DEBUG
- //#define NO_QUEUE_OBJECTS
- #endif
- #ifdef BF_GC_LOG_ENABLED
- static bf::System::Object* gMarkingObject[8192];
- #endif
- void BFGC::MarkFromGCThread(bf::System::Object* obj)
- {
- if (obj == NULL)
- return;
- //BP_ZONE("MarkFromGCThread");
-
- void* addr = obj;
- if ((addr < tcmalloc_obj::PageHeap::sAddressStart) || (addr >= tcmalloc_obj::PageHeap::sAddressEnd))
- return;
- tcmalloc_obj::Span* span = TCGetSpanAt(obj);
- if (span == NULL)
- return;
- if (span->location != tcmalloc_obj::Span::IN_USE)
- return;
-
- intptr pageSize = (intptr) 1 << kPageShift;
- int spanSize = pageSize * span->length;
- void* spanStart = (void*)((intptr)span->start << kPageShift);
- void* spanEnd = (void*)((intptr)spanStart + spanSize);
-
- if ((addr < spanStart) || (addr > (uint8*)spanEnd - sizeof(bf::System::Object)))
- return;
-
- // Is it already marked? Ignore.
- if ((obj->mObjectFlags & BF_OBJECTFLAG_MARK_ID_MASK) == mCurMarkId)
- return;
- // Don't do any processing of non-allocated objects (like string literals), or append allocs
- if ((obj->mObjectFlags & BF_OBJECTFLAG_ALLOCATED) == 0)
- return;
- if (obj->mAllocCheckPtr == 0) // It IS in the heap but not allocated
- return;
- bool curIsDeleted = false;
- if (mMarkingDeleted)
- {
- if ((obj->mObjectFlags & BF_OBJECTFLAG_DELETED) == 0)
- {
- // Don't allow a deleted object to mark a non-deleted object-
- // That should be handled as a LEAK if there aren't any non-deleted objects referencing it
- return;
- }
- }
- int elementSize = Static::sizemap()->ByteSizeForClass(span->sizeclass);
- // Large alloc
- if (elementSize == 0)
- {
- if (obj != (bf::System::Object*)spanStart)
- return;
- }
- else
- {
- void* maskedAddr = addr;
- maskedAddr = (uint8*)spanStart + (((uint8*)maskedAddr - (uint8*)spanStart) / elementSize * elementSize);
- if (obj != (bf::System::Object*)maskedAddr)
- return;
- }
-
- #ifndef BF_GC_DISABLED
- BF_LOGASSERT(mThreadId == BfpThread_GetCurrentId());
- BF_LOGASSERT(obj->mClassVData != 0);
- #ifdef TARGET_TYPE
- if (obj->mBFVData->mType == TARGET_TYPE)
- {
- gMarkTargetCount++;
- printf("Marking target %p\n", obj);
- }
- #endif
-
- #ifdef BF_GC_LOG_ENABLED
- bf::System::Object* parentObj = NULL;
- if (mMarkDepthCount > 0)
- parentObj = gMarkingObject[mMarkDepthCount-1];
- BFLOG3(GCLog::EVENT_MARK, (intptr)obj, obj->mObjectFlags, (intptr)parentObj);
- #endif
-
- int maxMarkDepth = 256;
- //int maxMarkDepth = 1;
- obj->mObjectFlags = (BfObjectFlags)((obj->mObjectFlags & ~BF_OBJECTFLAG_MARK_ID_MASK) | mCurMarkId);
- mCurGCMarkCount++;
- //if (obj->mBFVData->BFMarkMembers != NULL)
- {
- mCurGCObjectQueuedCount++;
- #ifdef NO_QUEUE_OBJECTS
- Beefy::AutoCrit autoCrit(mCritSect);
- BFVCALL(obj, BFMarkMembers)();
- #else
- bool allowQueue = true;
- //if ((!mQueueMarkObjects) && (mMarkDepthCount < maxMarkDepth))
- if (mMarkDepthCount < maxMarkDepth)
- {
- #ifdef BF_GC_LOG_ENABLED
- gMarkingObject[mMarkDepthCount] = obj;
- #endif
- mMarkDepthCount++;
- // Try to clear off the list first
- while (!mPendingGCData.IsEmpty())
- {
- bf::System::Object* queuedObj = mPendingGCData.back();
- mPendingGCData.pop_back();
- MarkMembers(queuedObj);
- }
- MarkMembers(obj);
- mMarkDepthCount--;
- }
- else
- {
- if (mPendingGCData.GetFreeCount() > 0)
- {
- mPendingGCData.Add(obj);
- }
- else
- {
- // No more room left -- we can't queue...
- MarkMembers(obj);
- }
- }
- #endif
- }
- #endif
- }
- void BFGC::SetAutoCollectPeriod(int periodMS)
- {
- mFullGCPeriod = periodMS;
- mCollectEvent.Set();
- }
- void BFGC::SetCollectFreeThreshold(int freeBytes)
- {
- mFreeTrigger = freeBytes;
- mCollectEvent.Set();
- }
- void BFGC::SetMaxPausePercentage(int maxPausePercentage)
- {
- mMaxPausePercentage = maxPausePercentage;
- mCollectEvent.Set();
- }
- void BFGC::SetMaxRawDeferredObjectFreePercentage(intptr maxPercentage)
- {
- mMaxRawDeferredObjectFreePercentage = maxPercentage;
- }
- using namespace bf::System;
- void GC::Run()
- {
- #ifdef BF_GC_INCREMENTAL
- gBFGC.Run();
- #endif
- }
- void GC::Init()
- {
- gBFGC.Init();
- }
- void GC::ReportTLSMember(intptr tlsIndex, void* ptr, void* markFunc)
- {
- gBFGC.ReportTLSMember((int)tlsIndex, ptr, markFunc);
- }
- void GC::StopCollecting()
- {
- gBFGC.StopCollecting();
- }
- void GC::AddStackMarkableObject(Object* obj)
- {
- gBFGC.AddStackMarkableObject(obj);
- }
- void GC::RemoveStackMarkableObject(Object* obj)
- {
- gBFGC.RemoveStackMarkableObject(obj);
- }
- void GC::Shutdown()
- {
- gBFGC.Shutdown();
- }
- void GC::Collect(bool async)
- {
- gBFGC.Collect(async);
- }
- void GC::Report()
- {
- gBFGC.Report();
- }
- void GC::Mark(Object* obj)
- {
- gBFGC.MarkFromGCThread(obj);
- }
- void GC::Mark(void* ptr, intptr size)
- {
- gBFGC.ConservativeScan(ptr, (int)size);
- }
- void GC::DebugDumpLeaks()
- {
- gBFGC.DebugDumpLeaks();
- }
- void GC::SetAutoCollectPeriod(intptr periodMS)
- {
- gBFGC.SetAutoCollectPeriod((int)periodMS);
- }
- void GC::SetCollectFreeThreshold(intptr freeBytes)
- {
- gBFGC.SetCollectFreeThreshold((int)freeBytes);
- }
- BFRT_EXPORT void bf::System::GC::SetMaxPausePercentage(intptr maxPausePercentage)
- {
- gBFGC.SetMaxPausePercentage(maxPausePercentage);
- }
- BFRT_EXPORT void bf::System::GC::SetMaxRawDeferredObjectFreePercentage(intptr maxPercentage)
- {
- gBFGC.SetMaxRawDeferredObjectFreePercentage(maxPercentage);
- }
- #else // BF_GC_SUPPORTED
- void* BfObjectAllocate(intptr size, bf::System::Type* type)
- {
- BF_FATAL("Not supported");
- return NULL;
- }
- using namespace bf::System;
- void GC::Run()
- {
- #ifdef BF_GC_INCREMENTAL
- gBFGC.Run();
- #endif
- }
- void GC::Init()
- {
- }
- void GC::ReportTLSMember(intptr tlsIndex, void* ptr, void* markFunc)
- {
- }
- void GC::Shutdown()
- {
- }
- void GC::Collect(bool async)
- {
- }
- void GC::Report()
- {
- }
- void GC::Mark(Object* obj)
- {
- }
- void GC::Mark(void* ptr, intptr size)
- {
- }
- void GC::DebugDumpLeaks()
- {
- }
- void GC::SetAutoCollectPeriod(intptr periodMS)
- {
- }
- void GC::SetCollectFreeThreshold(intptr freeBytes)
- {
- }
- #endif
|