mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-03-10 11:11:51 +00:00
GC fixes/improvements
- Have a real finalizer stage for destroying objects instead of mixing them in to the sweep stage. - Base GC rate on a running average of the allocation rate instead of basing it on the amount of time taken since the last sweep started. - More GC stats for better tweaking.
This commit is contained in:
parent
7e10138993
commit
7f899bd412
4 changed files with 381 additions and 173 deletions
|
@ -221,6 +221,11 @@ public:
|
||||||
Counter = 0;
|
Counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetAndClock()
|
||||||
|
{
|
||||||
|
Counter = -static_cast<int64_t>(rdtsc());
|
||||||
|
}
|
||||||
|
|
||||||
void Clock()
|
void Clock()
|
||||||
{
|
{
|
||||||
int64_t time = rdtsc();
|
int64_t time = rdtsc();
|
||||||
|
|
|
@ -82,23 +82,58 @@
|
||||||
** infinity, where each step performs a full collection.) You can also
|
** infinity, where each step performs a full collection.) You can also
|
||||||
** change this value dynamically.
|
** change this value dynamically.
|
||||||
*/
|
*/
|
||||||
#define DEFAULT_GCMUL 200 // GC runs 'double the speed' of memory allocation
|
#ifndef _DEBUG
|
||||||
|
#define DEFAULT_GCMUL 600 // GC runs gcmul% the speed of memory allocation
|
||||||
|
#else
|
||||||
|
// Higher in debug builds to account for the extra time spent freeing objects
|
||||||
|
#define DEFAULT_GCMUL 800
|
||||||
|
#endif
|
||||||
|
|
||||||
// Minimum step size
|
// Minimum step size
|
||||||
#define GCSTEPSIZE (sizeof(DObject) * 16)
|
#define GCMINSTEPSIZE (sizeof(DObject) * 16)
|
||||||
|
|
||||||
// Maximum number of elements to sweep in a single step
|
// Sweeps traverse objects in chunks of this size
|
||||||
#define GCSWEEPMAX 40
|
#define GCSWEEPGRANULARITY 40
|
||||||
|
|
||||||
// Cost of sweeping one element (the size of a small object divided by
|
// Cost of deleting an object
|
||||||
// some adjust for the sweep speed)
|
#ifndef _DEBUG
|
||||||
#define GCSWEEPCOST (sizeof(DObject) / 4)
|
#define GCDELETECOST 75
|
||||||
|
#else
|
||||||
|
// Freeing memory is much more costly in debug builds
|
||||||
|
#define GCDELETECOST 230
|
||||||
|
#endif
|
||||||
|
|
||||||
// Cost of calling of one destructor
|
// Cost of destroying an object
|
||||||
#define GCFINALIZECOST 100
|
#define GCDESTROYCOST 15
|
||||||
|
|
||||||
// TYPES -------------------------------------------------------------------
|
// TYPES -------------------------------------------------------------------
|
||||||
|
|
||||||
|
class FAveragizer
|
||||||
|
{
|
||||||
|
// Number of allocations to track
|
||||||
|
static inline constexpr unsigned HistorySize = 512;
|
||||||
|
|
||||||
|
size_t History[HistorySize];
|
||||||
|
size_t TotalAmount;
|
||||||
|
int TotalCount;
|
||||||
|
unsigned NewestPos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FAveragizer();
|
||||||
|
void AddAlloc(size_t alloc);
|
||||||
|
size_t GetAverage();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FStepStats
|
||||||
|
{
|
||||||
|
cycle_t Clock[GC::GCS_COUNT];
|
||||||
|
size_t BytesCovered[GC::GCS_COUNT];
|
||||||
|
int Count[GC::GCS_COUNT];
|
||||||
|
|
||||||
|
void Format(FString &out);
|
||||||
|
void Reset();
|
||||||
|
};
|
||||||
|
|
||||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||||
|
|
||||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||||
|
@ -114,28 +149,50 @@ static size_t CalcStepSize();
|
||||||
namespace GC
|
namespace GC
|
||||||
{
|
{
|
||||||
size_t AllocBytes;
|
size_t AllocBytes;
|
||||||
|
size_t RunningAllocBytes;
|
||||||
|
size_t RunningDeallocBytes;
|
||||||
size_t Threshold;
|
size_t Threshold;
|
||||||
size_t Estimate;
|
size_t Estimate;
|
||||||
DObject *Gray;
|
DObject *Gray;
|
||||||
DObject *Root;
|
DObject *Root;
|
||||||
DObject *SoftRoots;
|
DObject *SoftRoots;
|
||||||
DObject **SweepPos;
|
DObject **SweepPos;
|
||||||
|
DObject *ToDestroy;
|
||||||
uint32_t CurrentWhite = OF_White0 | OF_Fixed;
|
uint32_t CurrentWhite = OF_White0 | OF_Fixed;
|
||||||
EGCState State = GCS_Pause;
|
EGCState State = GCS_Pause;
|
||||||
int Pause = DEFAULT_GCPAUSE;
|
int Pause = DEFAULT_GCPAUSE;
|
||||||
int StepMul = DEFAULT_GCMUL;
|
int StepMul = DEFAULT_GCMUL;
|
||||||
int StepCount;
|
FStepStats StepStats;
|
||||||
uint64_t CheckTime;
|
FStepStats PrevStepStats;
|
||||||
bool FinalGC;
|
bool FinalGC;
|
||||||
|
bool HadToDestroy;
|
||||||
|
|
||||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||||
|
|
||||||
static int LastCollectTime; // Time last time collector finished
|
static FAveragizer AllocHistory;// Tracks allocation rate over time
|
||||||
static size_t LastCollectAlloc; // Memory allocation when collector finished
|
static cycle_t GCTime; // Track time spent in GC
|
||||||
static size_t MinStepSize; // Cover at least this much memory per step
|
|
||||||
|
|
||||||
// CODE --------------------------------------------------------------------
|
// CODE --------------------------------------------------------------------
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// CheckGC
|
||||||
|
//
|
||||||
|
// Check if it's time to collect, and do a collection step if it is.
|
||||||
|
// Also does some bookkeeping. Should be called fairly consistantly.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void CheckGC()
|
||||||
|
{
|
||||||
|
AllocHistory.AddAlloc(RunningAllocBytes);
|
||||||
|
RunningAllocBytes = 0;
|
||||||
|
if (State > GCS_Pause || AllocBytes >= Threshold)
|
||||||
|
{
|
||||||
|
Step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// SetThreshold
|
// SetThreshold
|
||||||
|
@ -146,7 +203,7 @@ static size_t MinStepSize; // Cover at least this much memory per step
|
||||||
|
|
||||||
void SetThreshold()
|
void SetThreshold()
|
||||||
{
|
{
|
||||||
Threshold = (Estimate / 100) * Pause;
|
Threshold = (std::min(Estimate, AllocBytes) / 100) * Pause;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -170,55 +227,72 @@ size_t PropagateMark()
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// SweepList
|
// SweepObjects
|
||||||
//
|
//
|
||||||
// Runs a limited sweep on a list, returning the position in the list just
|
// Runs a limited sweep on the object list, returning the number of bytes
|
||||||
// after the last object swept.
|
// swept.
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
static DObject **SweepList(DObject **p, size_t count, size_t *finalize_count)
|
static size_t SweepObjects(size_t count)
|
||||||
{
|
{
|
||||||
DObject *curr;
|
DObject *curr;
|
||||||
int deadmask = OtherWhite();
|
int deadmask = OtherWhite();
|
||||||
size_t finalized = 0;
|
size_t swept = 0;
|
||||||
|
|
||||||
while ((curr = *p) != NULL && count-- > 0)
|
while ((curr = *SweepPos) != nullptr && count-- > 0)
|
||||||
{
|
{
|
||||||
|
swept += curr->GetClass()->Size;
|
||||||
if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead?
|
if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead?
|
||||||
{
|
{
|
||||||
assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed));
|
assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed));
|
||||||
curr->MakeWhite(); // make it white (for next cycle)
|
curr->MakeWhite(); // make it white (for next cycle)
|
||||||
p = &curr->ObjNext;
|
SweepPos = &curr->ObjNext;
|
||||||
}
|
}
|
||||||
else // must erase 'curr'
|
else
|
||||||
{
|
{
|
||||||
assert(curr->IsDead());
|
assert(curr->IsDead());
|
||||||
*p = curr->ObjNext;
|
|
||||||
if (!(curr->ObjectFlags & OF_EuthanizeMe))
|
if (!(curr->ObjectFlags & OF_EuthanizeMe))
|
||||||
{ // The object must be destroyed before it can be finalized.
|
{ // The object must be destroyed before it can be deleted.
|
||||||
// Note that thinkers must already have been destroyed. If they get here without
|
curr->GCNext = ToDestroy;
|
||||||
// having been destroyed first, it means they somehow became unattached from the
|
ToDestroy = curr;
|
||||||
// thinker lists. If I don't maintain the invariant that all live thinkers must
|
SweepPos = &curr->ObjNext;
|
||||||
// be in a thinker list, then I need to add write barriers for every time a
|
}
|
||||||
// thinker pointer is changed. This seems easier and perfectly reasonable, since
|
else
|
||||||
// a live thinker that isn't on a thinker list isn't much of a thinker.
|
{ // must erase 'curr'
|
||||||
|
*SweepPos = curr->ObjNext;
|
||||||
// However, this can happen during deletion of the thinker list while cleaning up
|
curr->ObjectFlags |= OF_Cleanup;
|
||||||
// from a savegame error so we can't assume that any thinker that gets here is an error.
|
delete curr;
|
||||||
|
swept += GCDELETECOST;
|
||||||
curr->Destroy();
|
|
||||||
}
|
}
|
||||||
curr->ObjectFlags |= OF_Cleanup;
|
|
||||||
delete curr;
|
|
||||||
finalized++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (finalize_count != NULL)
|
return swept;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// DestroyObjects
|
||||||
|
//
|
||||||
|
// Destroys up to count objects on a list linked on GCNext, returning the
|
||||||
|
// size of objects destroyed, for updating the estimate.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
static size_t DestroyObjects(size_t count)
|
||||||
|
{
|
||||||
|
DObject *curr;
|
||||||
|
size_t bytes_destroyed = 0;
|
||||||
|
|
||||||
|
while ((curr = ToDestroy) != nullptr && count-- > 0)
|
||||||
{
|
{
|
||||||
*finalize_count = finalized;
|
assert(!(curr->ObjectFlags & OF_EuthanizeMe));
|
||||||
|
bytes_destroyed += curr->GetClass()->Size + GCDESTROYCOST;
|
||||||
|
ToDestroy = curr->GCNext;
|
||||||
|
curr->GCNext = nullptr;
|
||||||
|
curr->Destroy();
|
||||||
}
|
}
|
||||||
return p;
|
return bytes_destroyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -269,20 +343,14 @@ void MarkArray(DObject **obj, size_t count)
|
||||||
//
|
//
|
||||||
// CalcStepSize
|
// CalcStepSize
|
||||||
//
|
//
|
||||||
// Decide how big a step should be based, depending on how long it took to
|
// Decide how big a step should be, based on the current allocation rate.
|
||||||
// allocate up to the threshold from the amount left after the previous
|
|
||||||
// collection.
|
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
static size_t CalcStepSize()
|
static size_t CalcStepSize()
|
||||||
{
|
{
|
||||||
int time_passed = int(CheckTime - LastCollectTime);
|
size_t avg = AllocHistory.GetAverage();
|
||||||
auto alloc = min(LastCollectAlloc, Estimate);
|
return std::max<size_t>(GCMINSTEPSIZE, avg * StepMul / 100);
|
||||||
size_t bytes_gained = AllocBytes > alloc ? AllocBytes - alloc : 0;
|
|
||||||
return (StepMul > 0 && time_passed > 0)
|
|
||||||
? std::max<size_t>(GCSTEPSIZE, bytes_gained / time_passed * StepMul / 100)
|
|
||||||
: std::numeric_limits<size_t>::max() / 2; // no limit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -302,15 +370,18 @@ void AddMarkerFunc(GCMarkerFunc func)
|
||||||
|
|
||||||
static void MarkRoot()
|
static void MarkRoot()
|
||||||
{
|
{
|
||||||
Gray = NULL;
|
PrevStepStats = StepStats;
|
||||||
|
StepStats.Reset();
|
||||||
|
|
||||||
|
Gray = nullptr;
|
||||||
|
|
||||||
for (auto func : markers) func();
|
for (auto func : markers) func();
|
||||||
|
|
||||||
// Mark soft roots.
|
// Mark soft roots.
|
||||||
if (SoftRoots != NULL)
|
if (SoftRoots != nullptr)
|
||||||
{
|
{
|
||||||
DObject **probe = &SoftRoots->ObjNext;
|
DObject **probe = &SoftRoots->ObjNext;
|
||||||
while (*probe != NULL)
|
while (*probe != nullptr)
|
||||||
{
|
{
|
||||||
DObject *soft = *probe;
|
DObject *soft = *probe;
|
||||||
probe = &soft->ObjNext;
|
probe = &soft->ObjNext;
|
||||||
|
@ -322,7 +393,6 @@ static void MarkRoot()
|
||||||
}
|
}
|
||||||
// Time to propagate the marks.
|
// Time to propagate the marks.
|
||||||
State = GCS_Propagate;
|
State = GCS_Propagate;
|
||||||
StepCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -341,10 +411,21 @@ static void Atomic()
|
||||||
SweepPos = &Root;
|
SweepPos = &Root;
|
||||||
State = GCS_Sweep;
|
State = GCS_Sweep;
|
||||||
Estimate = AllocBytes;
|
Estimate = AllocBytes;
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we are about to start a sweep, establish a baseline minimum
|
//==========================================================================
|
||||||
// step size for how much memory we want to sweep each CheckGC().
|
//
|
||||||
MinStepSize = CalcStepSize();
|
// SweepDone
|
||||||
|
//
|
||||||
|
// Sets up the Destroy phase, if there are any dead objects that haven't
|
||||||
|
// been destroyed yet, or skips to the Done state.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
static void SweepDone()
|
||||||
|
{
|
||||||
|
HadToDestroy = ToDestroy != nullptr;
|
||||||
|
State = HadToDestroy ? GCS_Destroy : GCS_Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -364,7 +445,7 @@ static size_t SingleStep()
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case GCS_Propagate:
|
case GCS_Propagate:
|
||||||
if (Gray != NULL)
|
if (Gray != nullptr)
|
||||||
{
|
{
|
||||||
return PropagateMark();
|
return PropagateMark();
|
||||||
}
|
}
|
||||||
|
@ -375,22 +456,30 @@ static size_t SingleStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
case GCS_Sweep: {
|
case GCS_Sweep: {
|
||||||
size_t old = AllocBytes;
|
RunningDeallocBytes = 0;
|
||||||
size_t finalize_count;
|
size_t swept = SweepObjects(GCSWEEPGRANULARITY);
|
||||||
SweepPos = SweepList(SweepPos, GCSWEEPMAX, &finalize_count);
|
Estimate -= RunningDeallocBytes;
|
||||||
if (*SweepPos == NULL)
|
if (*SweepPos == nullptr)
|
||||||
{ // Nothing more to sweep?
|
{ // Nothing more to sweep?
|
||||||
State = GCS_Finalize;
|
SweepDone();
|
||||||
}
|
}
|
||||||
//assert(old >= AllocBytes);
|
return swept;
|
||||||
Estimate -= max<size_t>(0, old - AllocBytes);
|
|
||||||
return (GCSWEEPMAX - finalize_count) * GCSWEEPCOST + finalize_count * GCFINALIZECOST;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case GCS_Finalize:
|
case GCS_Destroy: {
|
||||||
|
size_t destroy_size;
|
||||||
|
destroy_size = DestroyObjects(GCSWEEPGRANULARITY);
|
||||||
|
Estimate -= destroy_size;
|
||||||
|
if (ToDestroy == nullptr)
|
||||||
|
{ // Nothing more to destroy?
|
||||||
|
State = GCS_Done;
|
||||||
|
}
|
||||||
|
return destroy_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GCS_Done:
|
||||||
State = GCS_Pause; // end collection
|
State = GCS_Pause; // end collection
|
||||||
LastCollectAlloc = AllocBytes;
|
SetThreshold();
|
||||||
LastCollectTime = (int)CheckTime;
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -403,21 +492,27 @@ static size_t SingleStep()
|
||||||
//
|
//
|
||||||
// Step
|
// Step
|
||||||
//
|
//
|
||||||
// Performs enough single steps to cover GCSTEPSIZE * StepMul% bytes of
|
// Performs enough single steps to cover <StepSize> bytes of memory.
|
||||||
// memory.
|
// Some of those bytes might be "fake" to account for the cost of freeing
|
||||||
|
// or destroying object.
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void Step()
|
void Step()
|
||||||
{
|
{
|
||||||
// We recalculate a step size in case the rate of allocation went up
|
GCTime.ResetAndClock();
|
||||||
// since we started sweeping because we don't want to fall behind.
|
|
||||||
// However, we also don't want to go slower than what was decided upon
|
auto enter_state = State;
|
||||||
// when the sweep began if the rate of allocation has slowed.
|
StepStats.Count[enter_state]++;
|
||||||
size_t lim = max(CalcStepSize(), MinStepSize);
|
StepStats.Clock[enter_state].Clock();
|
||||||
|
|
||||||
|
size_t did = 0;
|
||||||
|
size_t lim = CalcStepSize();
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
size_t done = SingleStep();
|
size_t done = SingleStep();
|
||||||
|
did += done;
|
||||||
if (done < lim)
|
if (done < lim)
|
||||||
{
|
{
|
||||||
lim -= done;
|
lim -= done;
|
||||||
|
@ -426,17 +521,23 @@ void Step()
|
||||||
{
|
{
|
||||||
lim = 0;
|
lim = 0;
|
||||||
}
|
}
|
||||||
|
if (State != enter_state)
|
||||||
|
{
|
||||||
|
// Finish stats on old state
|
||||||
|
StepStats.Clock[enter_state].Unclock();
|
||||||
|
StepStats.BytesCovered[enter_state] += did;
|
||||||
|
|
||||||
|
// Start stats on new state
|
||||||
|
did = 0;
|
||||||
|
enter_state = State;
|
||||||
|
StepStats.Clock[enter_state].Clock();
|
||||||
|
StepStats.Count[enter_state]++;
|
||||||
|
}
|
||||||
} while (lim && State != GCS_Pause);
|
} while (lim && State != GCS_Pause);
|
||||||
if (State != GCS_Pause)
|
|
||||||
{
|
StepStats.Clock[enter_state].Unclock();
|
||||||
Threshold = AllocBytes;
|
StepStats.BytesCovered[enter_state] += did;
|
||||||
}
|
GCTime.Unclock();
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(AllocBytes >= Estimate);
|
|
||||||
SetThreshold();
|
|
||||||
}
|
|
||||||
StepCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -454,20 +555,23 @@ void FullGC()
|
||||||
// Reset sweep mark to sweep all elements (returning them to white)
|
// Reset sweep mark to sweep all elements (returning them to white)
|
||||||
SweepPos = &Root;
|
SweepPos = &Root;
|
||||||
// Reset other collector lists
|
// Reset other collector lists
|
||||||
Gray = NULL;
|
Gray = nullptr;
|
||||||
State = GCS_Sweep;
|
State = GCS_Sweep;
|
||||||
}
|
}
|
||||||
// Finish any pending sweep phase
|
// Finish any pending GC stages
|
||||||
while (State != GCS_Finalize)
|
|
||||||
{
|
|
||||||
SingleStep();
|
|
||||||
}
|
|
||||||
MarkRoot();
|
|
||||||
while (State != GCS_Pause)
|
while (State != GCS_Pause)
|
||||||
{
|
{
|
||||||
SingleStep();
|
SingleStep();
|
||||||
}
|
}
|
||||||
SetThreshold();
|
// Loop until everything that can be destroyed and freed is
|
||||||
|
do
|
||||||
|
{
|
||||||
|
MarkRoot();
|
||||||
|
while (State != GCS_Pause)
|
||||||
|
{
|
||||||
|
SingleStep();
|
||||||
|
}
|
||||||
|
} while (HadToDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -481,9 +585,9 @@ void FullGC()
|
||||||
|
|
||||||
void Barrier(DObject *pointing, DObject *pointed)
|
void Barrier(DObject *pointing, DObject *pointed)
|
||||||
{
|
{
|
||||||
assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead()));
|
assert(pointing == nullptr || (pointing->IsBlack() && !pointing->IsDead()));
|
||||||
assert(pointed->IsWhite() && !pointed->IsDead());
|
assert(pointed->IsWhite() && !pointed->IsDead());
|
||||||
assert(State != GCS_Finalize && State != GCS_Pause);
|
assert(State != GCS_Destroy && State != GCS_Pause);
|
||||||
assert(!(pointed->ObjectFlags & OF_Released)); // if a released object gets here, something must be wrong.
|
assert(!(pointed->ObjectFlags & OF_Released)); // if a released object gets here, something must be wrong.
|
||||||
if (pointed->ObjectFlags & OF_Released) return; // don't do anything with non-GC'd objects.
|
if (pointed->ObjectFlags & OF_Released) return; // don't do anything with non-GC'd objects.
|
||||||
// The invariant only needs to be maintained in the propagate state.
|
// The invariant only needs to be maintained in the propagate state.
|
||||||
|
@ -495,7 +599,7 @@ void Barrier(DObject *pointing, DObject *pointed)
|
||||||
}
|
}
|
||||||
// In other states, we can mark the pointing object white so this
|
// In other states, we can mark the pointing object white so this
|
||||||
// barrier won't be triggered again, saving a few cycles in the future.
|
// barrier won't be triggered again, saving a few cycles in the future.
|
||||||
else if (pointing != NULL)
|
else if (pointing != nullptr)
|
||||||
{
|
{
|
||||||
pointing->MakeWhite();
|
pointing->MakeWhite();
|
||||||
}
|
}
|
||||||
|
@ -503,13 +607,13 @@ void Barrier(DObject *pointing, DObject *pointed)
|
||||||
|
|
||||||
void DelSoftRootHead()
|
void DelSoftRootHead()
|
||||||
{
|
{
|
||||||
if (SoftRoots != NULL)
|
if (SoftRoots != nullptr)
|
||||||
{
|
{
|
||||||
// Don't let the destructor print a warning message
|
// Don't let the destructor print a warning message
|
||||||
SoftRoots->ObjectFlags |= OF_YesReallyDelete;
|
SoftRoots->ObjectFlags |= OF_YesReallyDelete;
|
||||||
delete SoftRoots;
|
delete SoftRoots;
|
||||||
}
|
}
|
||||||
SoftRoots = NULL;
|
SoftRoots = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -526,7 +630,7 @@ void AddSoftRoot(DObject *obj)
|
||||||
DObject **probe;
|
DObject **probe;
|
||||||
|
|
||||||
// Are there any soft roots yet?
|
// Are there any soft roots yet?
|
||||||
if (SoftRoots == NULL)
|
if (SoftRoots == nullptr)
|
||||||
{
|
{
|
||||||
// Create a new object to root the soft roots off of, and stick
|
// Create a new object to root the soft roots off of, and stick
|
||||||
// it at the end of the object list, so we know that anything
|
// it at the end of the object list, so we know that anything
|
||||||
|
@ -534,17 +638,17 @@ void AddSoftRoot(DObject *obj)
|
||||||
SoftRoots = Create<DObject>();
|
SoftRoots = Create<DObject>();
|
||||||
SoftRoots->ObjectFlags |= OF_Fixed;
|
SoftRoots->ObjectFlags |= OF_Fixed;
|
||||||
probe = &Root;
|
probe = &Root;
|
||||||
while (*probe != NULL)
|
while (*probe != nullptr)
|
||||||
{
|
{
|
||||||
probe = &(*probe)->ObjNext;
|
probe = &(*probe)->ObjNext;
|
||||||
}
|
}
|
||||||
Root = SoftRoots->ObjNext;
|
Root = SoftRoots->ObjNext;
|
||||||
SoftRoots->ObjNext = NULL;
|
SoftRoots->ObjNext = nullptr;
|
||||||
*probe = SoftRoots;
|
*probe = SoftRoots;
|
||||||
}
|
}
|
||||||
// Mark this object as rooted and move it after the SoftRoots marker.
|
// Mark this object as rooted and move it after the SoftRoots marker.
|
||||||
probe = &Root;
|
probe = &Root;
|
||||||
while (*probe != NULL && *probe != obj)
|
while (*probe != nullptr && *probe != obj)
|
||||||
{
|
{
|
||||||
probe = &(*probe)->ObjNext;
|
probe = &(*probe)->ObjNext;
|
||||||
}
|
}
|
||||||
|
@ -567,14 +671,14 @@ void DelSoftRoot(DObject *obj)
|
||||||
{
|
{
|
||||||
DObject **probe;
|
DObject **probe;
|
||||||
|
|
||||||
if (!(obj->ObjectFlags & OF_Rooted))
|
if (obj == nullptr || !(obj->ObjectFlags & OF_Rooted))
|
||||||
{ // Not rooted, so nothing to do.
|
{ // Not rooted, so nothing to do.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
obj->ObjectFlags &= ~OF_Rooted;
|
obj->ObjectFlags &= ~OF_Rooted;
|
||||||
// Move object out of the soft roots part of the list.
|
// Move object out of the soft roots part of the list.
|
||||||
probe = &SoftRoots;
|
probe = &SoftRoots;
|
||||||
while (*probe != NULL && *probe != obj)
|
while (*probe != nullptr && *probe != obj)
|
||||||
{
|
{
|
||||||
probe = &(*probe)->ObjNext;
|
probe = &(*probe)->ObjNext;
|
||||||
}
|
}
|
||||||
|
@ -588,6 +692,52 @@ void DelSoftRoot(DObject *obj)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FAveragizer - Constructor
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FAveragizer::FAveragizer()
|
||||||
|
{
|
||||||
|
NewestPos = 0;
|
||||||
|
TotalAmount = 0;
|
||||||
|
TotalCount = 0;
|
||||||
|
memset(History, 0, sizeof(History));
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FAveragizer :: AddAlloc
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FAveragizer::AddAlloc(size_t alloc)
|
||||||
|
{
|
||||||
|
NewestPos = (NewestPos + 1) & (HistorySize - 1);
|
||||||
|
if (TotalCount < HistorySize)
|
||||||
|
{
|
||||||
|
TotalCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TotalAmount -= History[NewestPos];
|
||||||
|
}
|
||||||
|
History[NewestPos] = alloc;
|
||||||
|
TotalAmount += alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FAveragizer :: GetAverage
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
size_t FAveragizer::GetAverage()
|
||||||
|
{
|
||||||
|
return TotalCount != 0 ? TotalAmount / TotalCount : 0;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// STAT gc
|
// STAT gc
|
||||||
|
@ -602,18 +752,66 @@ ADD_STAT(gc)
|
||||||
" Pause ",
|
" Pause ",
|
||||||
"Propagate",
|
"Propagate",
|
||||||
" Sweep ",
|
" Sweep ",
|
||||||
"Finalize " };
|
" Destroy ",
|
||||||
|
" Done "
|
||||||
|
};
|
||||||
FString out;
|
FString out;
|
||||||
out.Format("[%s] Alloc:%6zuK Thresh:%6zuK Est:%6zuK Steps: %d %zuK",
|
double time = GC::State != GC::GCS_Pause ? GC::GCTime.TimeMS() : 0;
|
||||||
|
|
||||||
|
GC::PrevStepStats.Format(out);
|
||||||
|
out << "\n";
|
||||||
|
GC::StepStats.Format(out);
|
||||||
|
out.AppendFormat("\n%.2fms [%s] Rate:%3zuK (%3zuK) Alloc:%6zuK Est:%6zuK Thresh:%6zuK",
|
||||||
|
time,
|
||||||
StateStrings[GC::State],
|
StateStrings[GC::State],
|
||||||
|
(GC::AllocHistory.GetAverage() + 1023) >> 10,
|
||||||
|
(GC::CalcStepSize() + 1023) >> 10,
|
||||||
(GC::AllocBytes + 1023) >> 10,
|
(GC::AllocBytes + 1023) >> 10,
|
||||||
(GC::Threshold + 1023) >> 10,
|
|
||||||
(GC::Estimate + 1023) >> 10,
|
(GC::Estimate + 1023) >> 10,
|
||||||
GC::StepCount,
|
(GC::Threshold + 1023) >> 10);
|
||||||
(GC::MinStepSize + 1023) >> 10);
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FStepStats :: Reset
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStepStats::Reset()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < countof(Count); ++i)
|
||||||
|
{
|
||||||
|
Count[i] = 0;
|
||||||
|
BytesCovered[i] = 0;
|
||||||
|
Clock[i].Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FStepStats :: Format
|
||||||
|
//
|
||||||
|
// Appends its stats to the given FString.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStepStats::Format(FString &out)
|
||||||
|
{
|
||||||
|
// Because everything in the default green is hard to distinguish,
|
||||||
|
// each stage has its own color.
|
||||||
|
for (int i = GC::GCS_Propagate; i < GC::GCS_Done; ++i)
|
||||||
|
{
|
||||||
|
int count = Count[i];
|
||||||
|
double time = Clock[i].TimeMS();
|
||||||
|
out.AppendFormat(TEXTCOLOR_ESCAPESTR "%c[%c%6zuK %4d*%.2fms]",
|
||||||
|
"-NKB"[i], /* Color codes */
|
||||||
|
"-PSD"[i], /* Stage prefixes: (P)ropagate, (S)weep, (D)estroy */
|
||||||
|
(BytesCovered[i] + 1023) >> 10, count, count != 0 ? time / count : time);
|
||||||
|
}
|
||||||
|
out << TEXTCOLOR_GREEN;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// CCMD gc
|
// CCMD gc
|
||||||
|
|
|
@ -37,12 +37,21 @@ namespace GC
|
||||||
GCS_Pause,
|
GCS_Pause,
|
||||||
GCS_Propagate,
|
GCS_Propagate,
|
||||||
GCS_Sweep,
|
GCS_Sweep,
|
||||||
GCS_Finalize
|
GCS_Destroy,
|
||||||
|
GCS_Done,
|
||||||
|
|
||||||
|
GCS_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
// Number of bytes currently allocated through M_Malloc/M_Realloc.
|
// Number of bytes currently allocated through M_Malloc/M_Realloc.
|
||||||
extern size_t AllocBytes;
|
extern size_t AllocBytes;
|
||||||
|
|
||||||
|
// Number of bytes allocated since last collection step.
|
||||||
|
extern size_t RunningAllocBytes;
|
||||||
|
|
||||||
|
// Number of bytes freed since last collection step.
|
||||||
|
extern size_t RunningDeallocBytes;
|
||||||
|
|
||||||
// Amount of memory to allocate before triggering a collection.
|
// Amount of memory to allocate before triggering a collection.
|
||||||
extern size_t Threshold;
|
extern size_t Threshold;
|
||||||
|
|
||||||
|
@ -70,18 +79,12 @@ namespace GC
|
||||||
// Is this the final collection just before exit?
|
// Is this the final collection just before exit?
|
||||||
extern bool FinalGC;
|
extern bool FinalGC;
|
||||||
|
|
||||||
// Counts the number of times CheckGC has been called.
|
|
||||||
extern uint64_t CheckTime;
|
|
||||||
|
|
||||||
// Current white value for known-dead objects.
|
// Current white value for known-dead objects.
|
||||||
static inline uint32_t OtherWhite()
|
static inline uint32_t OtherWhite()
|
||||||
{
|
{
|
||||||
return CurrentWhite ^ OF_WhiteBits;
|
return CurrentWhite ^ OF_WhiteBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frees all objects, whether they're dead or not.
|
|
||||||
void FreeAll();
|
|
||||||
|
|
||||||
// Does one collection step.
|
// Does one collection step.
|
||||||
void Step();
|
void Step();
|
||||||
|
|
||||||
|
@ -118,12 +121,7 @@ namespace GC
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's time to collect, and do a collection step if it is.
|
// Check if it's time to collect, and do a collection step if it is.
|
||||||
static inline void CheckGC()
|
void CheckGC();
|
||||||
{
|
|
||||||
CheckTime++;
|
|
||||||
if (AllocBytes >= Threshold)
|
|
||||||
Step();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forces a collection to start now.
|
// Forces a collection to start now.
|
||||||
static inline void StartCollection()
|
static inline void StartCollection()
|
||||||
|
@ -176,6 +174,32 @@ namespace GC
|
||||||
using GCMarkerFunc = void(*)();
|
using GCMarkerFunc = void(*)();
|
||||||
void AddMarkerFunc(GCMarkerFunc func);
|
void AddMarkerFunc(GCMarkerFunc func);
|
||||||
|
|
||||||
|
// Report an allocation to the GC
|
||||||
|
static inline void ReportAlloc(size_t alloc)
|
||||||
|
{
|
||||||
|
AllocBytes += alloc;
|
||||||
|
RunningAllocBytes += alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report a deallocation to the GC
|
||||||
|
static inline void ReportDealloc(size_t dealloc)
|
||||||
|
{
|
||||||
|
AllocBytes -= dealloc;
|
||||||
|
RunningDeallocBytes += dealloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report a reallocation to the GC
|
||||||
|
static inline void ReportRealloc(size_t oldsize, size_t newsize)
|
||||||
|
{
|
||||||
|
if (oldsize < newsize)
|
||||||
|
{
|
||||||
|
ReportAlloc(newsize - oldsize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReportDealloc(oldsize - newsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A template class to help with handling read barriers. It does not
|
// A template class to help with handling read barriers. It does not
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
** Wrappers for the malloc family of functions that count used bytes.
|
** Wrappers for the malloc family of functions that count used bytes.
|
||||||
**
|
**
|
||||||
**---------------------------------------------------------------------------
|
**---------------------------------------------------------------------------
|
||||||
** Copyright 1998-2008 Randy Heit
|
** Copyright 1998-2008 Marisa Heit
|
||||||
** All rights reserved.
|
** All rights reserved.
|
||||||
**
|
**
|
||||||
** Redistribution and use in source and binary forms, with or without
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "engineerrors.h"
|
#include "engineerrors.h"
|
||||||
#include "dobject.h"
|
#include "dobjgc.h"
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#define _NORMAL_BLOCK 0
|
#define _NORMAL_BLOCK 0
|
||||||
|
@ -59,25 +59,22 @@ void *M_Malloc(size_t size)
|
||||||
{
|
{
|
||||||
void *block = malloc(size);
|
void *block = malloc(size);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes", size);
|
I_FatalError("Could not malloc %zu bytes", size);
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc(void *memblock, size_t size)
|
void *M_Realloc(void *memblock, size_t size)
|
||||||
{
|
{
|
||||||
if (memblock != NULL)
|
size_t oldsize = memblock ? _msize(memblock) : 0;
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = realloc(memblock, size);
|
void *block = realloc(memblock, size);
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes", size);
|
I_FatalError("Could not realloc %zu bytes", size);
|
||||||
}
|
}
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -85,28 +82,25 @@ void *M_Malloc(size_t size)
|
||||||
{
|
{
|
||||||
void *block = malloc(size+sizeof(size_t));
|
void *block = malloc(size+sizeof(size_t));
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes", size);
|
I_FatalError("Could not malloc %zu bytes", size);
|
||||||
|
|
||||||
size_t *sizeStore = (size_t *) block;
|
size_t *sizeStore = (size_t *) block;
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc(void *memblock, size_t size)
|
void *M_Realloc(void *memblock, size_t size)
|
||||||
{
|
{
|
||||||
if(memblock == NULL)
|
if (memblock == nullptr)
|
||||||
return M_Malloc(size);
|
return M_Malloc(size);
|
||||||
|
|
||||||
if (memblock != NULL)
|
size_t oldsize = _msize(memblock);
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = realloc(((size_t*) memblock)-1, size+sizeof(size_t));
|
void *block = realloc(((size_t*) memblock)-1, size+sizeof(size_t));
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes", size);
|
I_FatalError("Could not realloc %zu bytes", size);
|
||||||
}
|
}
|
||||||
|
@ -115,7 +109,7 @@ void *M_Realloc(void *memblock, size_t size)
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -129,25 +123,22 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
void *block = _malloc_dbg(size, _NORMAL_BLOCK, file, lineno);
|
void *block = _malloc_dbg(size, _NORMAL_BLOCK, file, lineno);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
if (memblock != NULL)
|
size_t oldsize = memblock ? _msize(memblock) : 0;
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = _realloc_dbg(memblock, size, _NORMAL_BLOCK, file, lineno);
|
void *block = _realloc_dbg(memblock, size, _NORMAL_BLOCK, file, lineno);
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
}
|
}
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -155,29 +146,26 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
void *block = _malloc_dbg(size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
void *block = _malloc_dbg(size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
|
|
||||||
size_t *sizeStore = (size_t *) block;
|
size_t *sizeStore = (size_t *) block;
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
if(memblock == NULL)
|
if (memblock == nullptr)
|
||||||
return M_Malloc_Dbg(size, file, lineno);
|
return M_Malloc_Dbg(size, file, lineno);
|
||||||
|
|
||||||
if (memblock != NULL)
|
size_t oldsize = _msize(memblock);
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = _realloc_dbg(((size_t*) memblock)-1, size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
void *block = _realloc_dbg(((size_t*) memblock)-1, size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
}
|
}
|
||||||
|
@ -186,29 +174,22 @@ void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void M_Free (void *block)
|
||||||
|
{
|
||||||
|
if (block != nullptr)
|
||||||
|
{
|
||||||
|
GC::ReportDealloc(_msize(block));
|
||||||
#if !defined(__solaris__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
|
#if !defined(__solaris__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
|
||||||
void M_Free (void *block)
|
|
||||||
{
|
|
||||||
if (block != NULL)
|
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(block);
|
|
||||||
free(block);
|
free(block);
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
void M_Free (void *block)
|
|
||||||
{
|
|
||||||
if(block != NULL)
|
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(block);
|
|
||||||
free(((size_t*) block)-1);
|
free(((size_t*) block)-1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue