365 lines
10 KiB
C++
365 lines
10 KiB
C++
/*******************************************************************************
|
|
* pvmem.h
|
|
*
|
|
* Definitions for windows-specific memory handling routines.
|
|
*
|
|
* Author: Christopher J. Cason.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/windows/pvmem.h $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#ifndef __PVMEM_H__
|
|
#define __PVMEM_H__
|
|
|
|
#undef new
|
|
#undef delete
|
|
|
|
namespace povwin
|
|
{
|
|
|
|
#ifdef _WIN64
|
|
#define InterlockedExchangeAdd InterlockedExchangeAdd64
|
|
#define InterlockedIncrement InterlockedIncrement64
|
|
#endif
|
|
|
|
class WinMemStats
|
|
{
|
|
public:
|
|
WinMemStats() { Clear(); }
|
|
~WinMemStats() {}
|
|
|
|
void Clear() { currentAllocated = peakAllocated = callsToAlloc = callsToFree = smallestAlloc = largestAlloc = 0; }
|
|
void * operator new(size_t count) { return (WinMemStats *) HeapAlloc(GetProcessHeap(), 0, count); }
|
|
void operator delete (void *p) { HeapFree(GetProcessHeap(), 0, p); }
|
|
|
|
void RecordAlloc(size_t count)
|
|
{
|
|
callsToAlloc++;
|
|
currentAllocated += count;
|
|
if (currentAllocated > 0)
|
|
if (peakAllocated < currentAllocated)
|
|
peakAllocated = currentAllocated;
|
|
if (count > 1)
|
|
if ((smallestAlloc == 0) || (count < smallestAlloc))
|
|
smallestAlloc = count;
|
|
if (count > largestAlloc)
|
|
largestAlloc = count;
|
|
}
|
|
|
|
void RecordFree(size_t count)
|
|
{
|
|
callsToFree++;
|
|
currentAllocated -= count;
|
|
}
|
|
|
|
void InterlockedRecordAlloc(size_t count)
|
|
{
|
|
}
|
|
|
|
void InterlockedRecordFree(size_t count)
|
|
{
|
|
}
|
|
|
|
void Report(uint64& allocs, uint64& frees, int64& current, uint64& peak, uint64 &smallest, uint64 &largest)
|
|
{
|
|
allocs = callsToAlloc;
|
|
frees = callsToFree;
|
|
current = currentAllocated;
|
|
peak = peakAllocated;
|
|
smallest = smallestAlloc;
|
|
largest = largestAlloc;
|
|
}
|
|
|
|
WinMemStats& operator=(const WinMemStats& rhs)
|
|
{
|
|
// not interlocked, should only be used when thread is not performing memory allocations
|
|
currentAllocated = rhs.currentAllocated;
|
|
peakAllocated = rhs.peakAllocated;
|
|
callsToAlloc = rhs.callsToAlloc;
|
|
callsToFree = rhs.callsToFree;
|
|
smallestAlloc = rhs.smallestAlloc;
|
|
largestAlloc = rhs.largestAlloc;
|
|
return *this;
|
|
}
|
|
|
|
WinMemStats operator+(const WinMemStats& rhs) const
|
|
{
|
|
// not interlocked, should only be used when thread is not performing memory allocations
|
|
WinMemStats result = *this;
|
|
result.currentAllocated += rhs.currentAllocated;
|
|
result.callsToAlloc += rhs.callsToAlloc;
|
|
result.callsToFree += rhs.callsToFree;
|
|
result.peakAllocated = 0;
|
|
result.smallestAlloc = 0;
|
|
result.largestAlloc = 0;
|
|
return result;
|
|
}
|
|
|
|
WinMemStats& operator+=(const WinMemStats& rhs)
|
|
{
|
|
// not interlocked, should only be used when threads are not performing memory allocations
|
|
currentAllocated += rhs.currentAllocated;
|
|
callsToAlloc += rhs.callsToAlloc;
|
|
callsToFree += rhs.callsToFree;
|
|
peakAllocated = 0;
|
|
smallestAlloc = 0;
|
|
largestAlloc = 0;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
volatile int64 currentAllocated;
|
|
volatile uint64 peakAllocated;
|
|
volatile uint64 callsToAlloc;
|
|
volatile uint64 callsToFree;
|
|
volatile uint64 smallestAlloc;
|
|
volatile uint64 largestAlloc;
|
|
};
|
|
|
|
typedef WinMemStats PovMemStats;
|
|
|
|
class WinHeap
|
|
{
|
|
public:
|
|
WinHeap()
|
|
{
|
|
InitializeCriticalSectionAndSpinCount(&m_CritSec, 4000);
|
|
m_LowFragHeap = false;
|
|
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
|
|
// allocate heap
|
|
m_Heap = HeapCreate (HEAP_NO_SERIALIZE, si.dwPageSize, 0);
|
|
if (m_Heap == NULL)
|
|
return;
|
|
|
|
#if 0
|
|
// enable low-fragmentation heap. NB this only works on XP or later.
|
|
// TODO: make sure this isn't an unresolved symbol in W2K or earlier.
|
|
ULONG val = 2;
|
|
HeapSetInformation(m_Heap, HeapCompatibilityInformation, &val, sizeof(val));
|
|
if (HeapQueryInformation(m_Heap, HeapCompatibilityInformation, &val, sizeof(val), NULL))
|
|
if (val == 2)
|
|
m_LowFragHeap = true;
|
|
#endif
|
|
}
|
|
~WinHeap()
|
|
{
|
|
if (m_Heap != NULL)
|
|
{
|
|
Lock();
|
|
HeapDestroy(m_Heap);
|
|
Unlock();
|
|
}
|
|
DeleteCriticalSection(&m_CritSec);
|
|
}
|
|
|
|
void * operator new(size_t count) { return (WinHeap *) HeapAlloc(GetProcessHeap(), 0, count); }
|
|
bool Validate(const void *p = NULL) const { return m_Heap != NULL && HeapValidate(m_Heap, 0, p); }
|
|
void *Alloc(size_t count) { return HeapAlloc(m_Heap, 0, count ? count : 1); }
|
|
bool Free(void *p) { return HeapFree(m_Heap, 0, p); }
|
|
void *ReAlloc(void *p, size_t count) { return HeapReAlloc(m_Heap, 0, p, count); }
|
|
void Compact() { HeapCompact(m_Heap, 0); }
|
|
void Lock() { EnterCriticalSection(&m_CritSec); }
|
|
void Unlock() { LeaveCriticalSection(&m_CritSec); }
|
|
size_t BlockSize(const void *p) const { return HeapSize(m_Heap, 0, p); }
|
|
|
|
void *LockedAlloc(size_t count)
|
|
{
|
|
Lock();
|
|
void *p = HeapAlloc(m_Heap, 0, count ? count : 1);
|
|
Unlock();
|
|
return p;
|
|
}
|
|
|
|
void *LockedReAlloc(void *p, size_t count)
|
|
{
|
|
Lock();
|
|
p = HeapReAlloc(m_Heap, 0, p, count);
|
|
Unlock();
|
|
return p;
|
|
}
|
|
|
|
bool LockedFree(void *p)
|
|
{
|
|
Lock();
|
|
bool result = HeapFree(m_Heap, 0, p);
|
|
Unlock();
|
|
return result;
|
|
}
|
|
|
|
bool LockedValidate(const void *p = NULL)
|
|
{
|
|
if (m_Heap == NULL)
|
|
return false;
|
|
Lock();
|
|
bool result = HeapValidate(m_Heap, 0, p);
|
|
Unlock();
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
bool m_LowFragHeap;
|
|
HANDLE m_Heap;
|
|
CRITICAL_SECTION m_CritSec;
|
|
};
|
|
|
|
class HeapLock
|
|
{
|
|
public:
|
|
HeapLock(WinHeap *wh) : m_WinHeap(wh) { m_WinHeap->Lock(); }
|
|
~HeapLock() { m_WinHeap->Unlock(); }
|
|
private:
|
|
WinHeap *m_WinHeap;
|
|
};
|
|
|
|
class WinMemBlock
|
|
{
|
|
public:
|
|
__declspec(nothrow) WinMemBlock(const void *data, int line)
|
|
{
|
|
m_Sanity = 'PM';
|
|
m_Line = line;
|
|
m_Data = data;
|
|
# if EXTRA_VALIDATION
|
|
m_Hash = ptrdiff_t(m_Sanity) ^ ptrdiff_t(m_Line) ^ ptrdiff_t(m_Data) ^ ptrdiff_t(m_Length) ^ ptrdiff_t(this);
|
|
memcpy((char *) this + m_Length - sizeof(m_Hash), &m_Hash, sizeof(m_Hash));
|
|
# endif
|
|
}
|
|
|
|
// must be called with the lock held
|
|
void __declspec(nothrow) * operator new (size_t size, size_t len)
|
|
{
|
|
# if EXTRA_VALIDATION
|
|
WinMemBlock *block = (WinMemBlock *) m_Heap->Alloc(len + size + BlockPadding);
|
|
if (block == NULL)
|
|
return NULL;
|
|
block->m_Length = len + size + BlockPadding;
|
|
return block;
|
|
# else
|
|
return m_Heap->Alloc(len + size);
|
|
# endif
|
|
}
|
|
|
|
// must be called with the lock held
|
|
void __declspec(nothrow) operator delete (void *p)
|
|
{
|
|
m_Heap->Free(p);
|
|
}
|
|
|
|
bool Validate(bool full = EXTRA_VALIDATION) const
|
|
{
|
|
if (m_Sanity != 'PM')
|
|
return false;
|
|
# if EXTRA_VALIDATION
|
|
ptrdiff_t hash = ptrdiff_t(m_Sanity) ^ ptrdiff_t(m_Line) ^ ptrdiff_t(m_Data) ^ ptrdiff_t(m_Length) ^ ptrdiff_t(this);
|
|
if (m_Hash != hash || memcmp(&m_Hash, (char *) this + m_Length - sizeof(m_Hash), sizeof(m_Hash)) != 0)
|
|
return false;
|
|
# endif
|
|
if (!full)
|
|
return true;
|
|
if (m_Heap->Validate(this) == false)
|
|
return false;
|
|
# if EXTRA_VALIDATION
|
|
if (m_Heap->BlockSize(this) < m_Length)
|
|
return false;
|
|
# endif
|
|
return true;
|
|
}
|
|
|
|
size_t Size() const
|
|
{
|
|
size_t len = m_Heap->BlockSize(this);
|
|
if (len == (size_t) -1 || len < sizeof(WinMemBlock) + BlockPadding)
|
|
return (size_t) -1;
|
|
# if EXTRA_VALIDATION
|
|
if (len < m_Length)
|
|
return (size_t) -1;
|
|
# endif
|
|
return len;
|
|
}
|
|
|
|
size_t Size(size_t size) const
|
|
{
|
|
return size + sizeof(WinMemBlock) + BlockPadding;
|
|
}
|
|
|
|
void *GetMem()
|
|
{
|
|
return this + 1;
|
|
}
|
|
|
|
WinMemBlock *Realloc(size_t size, const void *data, int line)
|
|
{
|
|
WinMemBlock *newBlock = (WinMemBlock *) m_Heap->ReAlloc(this, size + sizeof(WinMemBlock) + BlockPadding);
|
|
if (newBlock == NULL)
|
|
return NULL;
|
|
// note that *this is no longer valid here!
|
|
newBlock->m_Line = line;
|
|
newBlock->m_Data = data;
|
|
# if EXTRA_VALIDATION
|
|
newBlock->m_Length = size + sizeof(WinMemBlock) + BlockPadding;
|
|
newBlock->m_Hash = ptrdiff_t(newBlock->m_Sanity) ^ ptrdiff_t(newBlock->m_Line) ^ ptrdiff_t(newBlock->m_Data) ^
|
|
ptrdiff_t(newBlock->m_Length) ^ ptrdiff_t(newBlock);
|
|
memcpy((char *) newBlock + newBlock->m_Length - sizeof(m_Hash), &newBlock->m_Hash, sizeof(m_Hash));
|
|
# endif
|
|
return newBlock;
|
|
}
|
|
|
|
static WinMemBlock *GetBlock(void *p, bool full = EXTRA_VALIDATION)
|
|
{
|
|
WinMemBlock *block = (WinMemBlock *) p - 1;
|
|
if (!block->Validate(full))
|
|
return NULL;
|
|
return block;
|
|
}
|
|
|
|
static void SetAllocator(WinHeap *alloc) { m_Heap = alloc; }
|
|
|
|
private:
|
|
unsigned short m_Sanity;
|
|
short m_Line;
|
|
const void *m_Data;
|
|
static WinHeap *m_Heap;
|
|
|
|
# if EXTRA_VALIDATION
|
|
size_t m_Length;
|
|
ptrdiff_t m_Hash;
|
|
static const size_t BlockPadding = sizeof(ptrdiff_t);
|
|
# else
|
|
static const size_t BlockPadding = 0;
|
|
# endif
|
|
|
|
WinMemBlock() {} // not available
|
|
void *operator new (size_t len) {} // not available
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|