/******************************************************************************* * pvmem.cpp * * This module implements 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 . * --------------------------------------------------------------------------- * 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.cpp $ * $Revision: #1 $ * $Change: 6069 $ * $DateTime: 2013/11/06 11:59:40 $ * $Author: chrisc $ *******************************************************************************/ #define POVWIN_FILE #define _WIN32_IE COMMONCTRL_VERSION #include #include #include #include #include "pvengine.h" #ifdef _DEBUG #define EXTRA_VALIDATION 1 #else #define EXTRA_VALIDATION 0 #endif #include "pvmem.h" // this must be the last file included #include "syspovdebug.h" extern "C" { int __cdecl WinMainCRTStartup(); } #undef new #undef delete using namespace pov_base; namespace povwin { bool TrackMem; __int64 PeakMem; static WinHeap *gHeap = NULL; WinHeap *WinMemBlock::m_Heap = NULL; static WinMemStats gMemStats; __declspec(thread) static WinMemStats *lMemStats = NULL; #if POV_MEM_STATS == 0 && WIN_MEM_TRACKING == 0 int __cdecl POVWINStartup() { return WinMainCRTStartup(); } void *win_malloc(size_t size) { void *result = malloc(size); if (result == NULL) throw std::bad_alloc(); return result; } void *win_malloc(size_t size, const void *data, int line) { void *result = malloc(size); if (result == NULL) throw std::bad_alloc(); return result; } void *win_calloc(size_t nelem, size_t size) { void *result = calloc(nelem, size); if (result == NULL) throw std::bad_alloc(); return result; } void *win_calloc(size_t nelem, size_t size, const void *data, int line) { void *result = calloc(nelem, size); if (result == NULL) throw std::bad_alloc(); return result; } void *win_realloc(void *p, size_t size) { void *result = realloc(p, size); if (result == NULL) throw std::bad_alloc(); return result; } void *win_realloc(void *p, size_t size, const void *data, int line) { void *result = realloc(p, size); if (result == NULL) throw std::bad_alloc(); return result; } void win_free(void *p) { free(p); } void win_free(void *p, const void *data, int line) { free(p); } char *win_strdup(const char *s) { char *result = _strdup(s); if (result == NULL) throw std::bad_alloc(); return result; } char *win_strdup(const char *s, const void *data, int line) { char *result = _strdup(s); if (result == NULL) throw std::bad_alloc(); return result; } void WinMemStage(bool BeginRender, void *cookie) { if (BeginRender) { PeakMem = 0; TrackMem = true; } else TrackMem = false; } bool WinMemReport(bool global, uint64& allocs, uint64& frees, int64& current, uint64& peak, uint64 &smallest, uint64 &largest) { if (!global) return false; allocs = frees = current = smallest = largest = 0; peak = PeakMem; return true; } void WinMemThreadStartup() { } void WinMemThreadCleanup() { } #else // This function is the first called when POVWIN starts (even before the CRT startup code). // Therefore we can't count on very much being set up for us. int __cdecl POVWINStartup() { // note that WinHeap has its own operator new. gHeap = new WinHeap; if (gHeap == NULL || gHeap->Validate() == false) { // not safe to call MessageBox here due to some other threads getting fired up // MessageBox(GetDesktopWindow(), "Failed to set up private heap - POV-Ray will now exit", "POV-Ray for Windows", MB_OK | MB_ICONERROR); return 1; } WinMemBlock::SetAllocator(gHeap); // we never delete gHeap since WinMainCRTStartup() often exits via means other than a return. return WinMainCRTStartup(); } void WinMemThreadStartup() { lMemStats = new WinMemStats(); } void WinMemThreadCleanup() { delete lMemStats; } bool WinMemReport(bool global, uint64& allocs, uint64& frees, int64& current, uint64& peak, uint64 &smallest, uint64 &largest) { HeapLock lock(gHeap); allocs = frees = current = peak = smallest = largest = 0; if (global) { gMemStats.Report(allocs, frees, current, peak, smallest, largest); return true; } if (lMemStats == NULL) return false; lMemStats->Report(allocs, frees, current, peak, smallest, largest); return true; } void WinMemStage(bool BeginRender, void *cookie) { HeapLock lock(gHeap); if (BeginRender) gMemStats.Clear(); } #ifdef _DEBUG void *win_malloc(size_t size, const void *data, int line) { HeapLock lock(gHeap); // note that WinMemBlock has its own operator new WinMemBlock *block = new (size) WinMemBlock(data, line); if (block == NULL) throw std::bad_alloc(); gMemStats.RecordAlloc(block->Size(size)); return block->GetMem(); } void *win_realloc(void *p, size_t newSize, const void *data, int line) { if (p == NULL) return win_malloc(newSize, data, line); if (newSize != 0) { HeapLock lock(gHeap); size_t oldSize; WinMemBlock *block = WinMemBlock::GetBlock(p); if ((block == NULL) || ((oldSize = block->Size()) == (size_t) -1)) throw POV_EXCEPTION(kCannotHandleDataErr, "Bad memory block in realloc()"); if ((block = block->Realloc(newSize, data, line)) == NULL) throw std::bad_alloc(); gMemStats.RecordFree(oldSize); gMemStats.RecordAlloc(block->Size(newSize)); return block->GetMem(); } else { win_free(p, data, line); return NULL; } } void win_free(void *p, const void *data, int line) { if (p == NULL) return; HeapLock lock(gHeap); size_t size; WinMemBlock *block = WinMemBlock::GetBlock(p); if ((block == NULL) || ((size = block->Size()) == (size_t) -1)) throw POV_EXCEPTION(kCannotHandleDataErr, "Bad memory block in free()"); gMemStats.RecordFree(size); // note that WinMemBlock has a private delete operator delete block; } void *win_calloc(size_t nelem, size_t size, const void *data, int line) { size_t count = size * nelem; void *p = win_malloc(count, data, line); if (p == NULL) throw std::bad_alloc(); memset(p, 0, count); return p; } char *win_strdup(const char *s, const void *data, int line) { char *p = (char *) win_malloc(strlen(s) + 1, data, line); strcpy(p, s); return p; } #else // _DEBUG // release-mode functions void *win_malloc(size_t size) { HeapLock lock(gHeap); void *p = gHeap->Alloc(size); if (p == NULL) throw std::bad_alloc(); return p; } void *win_realloc(void *p, size_t size) { if (p == NULL) return win_malloc(size); if (size == 0) { win_free(p); return NULL; } HeapLock lock(gHeap); size_t currentSize = gHeap->BlockSize(p); if (currentSize == (size_t) -1) throw POV_EXCEPTION(kCannotHandleDataErr, "Bad memory block in realloc()"); void *newBlock = gHeap->ReAlloc(p, size); if (newBlock == NULL) { // this can happen with the low-fragmentation heap, even if memory is available // as long as it's a shrink, we return the original pointer if (size < currentSize) return p; throw std::bad_alloc(); } gMemStats.RecordFree(currentSize); gMemStats.RecordAlloc(size); return newBlock; } void win_free(void *p) { if (p == NULL) return; HeapLock lock(gHeap); size_t len = gHeap->BlockSize(p); if (len == (size_t) -1) throw POV_EXCEPTION(kCannotHandleDataErr, "Bad memory block in free()"); gMemStats.RecordFree(len); if (!gHeap->Free(p)) throw POV_EXCEPTION(kCannotHandleDataErr, "Bad memory block in free()"); } void *win_calloc(size_t nelem, size_t size) { size_t count = size * nelem; void *p = win_malloc(count); if (p == NULL) return NULL; memset(p, 0, count); return p; } char *win_strdup(const char *s) { size_t len = strlen(s) + 1; char *p = (char *) win_malloc(len); memcpy(p, s, len); return p; } #endif // else portion of '#ifdef _DEBUG' #endif // else portion of '#if POV_MEM_STATS == 0 && WIN_MEM_TRACKING == 0' } // end of namespace povwin #if WIN_MEM_TRACKING using namespace povwin; #ifdef _DEBUG #define DEBUGPARAMS , data, line #else #define DEBUGPARAMS #endif void *operator new (size_t size, const char *func, const void *data, int line) { void *res; if ((res = win_malloc(size DEBUGPARAMS)) != NULL) return res; throw std::bad_alloc () ; } void *operator new[] (size_t size, const char *func, const void *data, int line) { void *res; if ((res = win_malloc(size DEBUGPARAMS)) != NULL) return res; throw std::bad_alloc () ; } void operator delete (void *p, const char *func, const void *data, int line) { win_free(p DEBUGPARAMS); } void operator delete[] (void *p, const char *func, const void *data, int line) { win_free(p DEBUGPARAMS); } #ifdef _DEBUG #undef DEBUGPARAMS #define DEBUGPARAMS , ReturnAddress(), -1 #endif void *operator new (size_t size) { void *res; if ((res = win_malloc(size DEBUGPARAMS)) != NULL) return res; throw std::bad_alloc () ; } void *operator new[] (size_t size) { void *res; if ((res = win_malloc(size DEBUGPARAMS)) != NULL) return res; throw std::bad_alloc () ; } void operator delete (void *p) throw () { win_free(p DEBUGPARAMS); } void operator delete[] (void *p) throw () { win_free(p DEBUGPARAMS); } #endif // #if WIN_MEM_TRACKING