/* * Copyright 1997 Marcus Meissner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #include "oleauto.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(olemalloc); static const IMallocVtbl allocator_vtbl; struct allocator { IMalloc IMalloc_iface; IMallocSpy *spy; DWORD spyed_allocations; BOOL spy_release_pending; /* CoRevokeMallocSpy called with spyed allocations left */ void **blocks; DWORD blocks_length; }; static struct allocator allocator = { .IMalloc_iface.lpVtbl = &allocator_vtbl }; static CRITICAL_SECTION allocspy_cs; static CRITICAL_SECTION_DEBUG allocspy_cs_debug = { 0, 0, &allocspy_cs, { &allocspy_cs_debug.ProcessLocksList, &allocspy_cs_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": allocspy_cs") } }; static CRITICAL_SECTION allocspy_cs = { &allocspy_cs_debug, -1, 0, 0, 0, 0 }; static BOOL mallocspy_grow(DWORD length) { void **blocks; if (!allocator.blocks) blocks = LocalAlloc(LMEM_ZEROINIT, length * sizeof(void *)); else blocks = LocalReAlloc(allocator.blocks, length * sizeof(void *), LMEM_ZEROINIT | LMEM_MOVEABLE); if (blocks) { allocator.blocks = blocks; allocator.blocks_length = length; } return blocks != NULL; } static void mallocspy_add_mem(void *mem) { void **current; if (!mem || (!allocator.blocks_length && !mallocspy_grow(0x1000))) return; /* Find a free location */ current = allocator.blocks; while (*current) { current++; if (current >= allocator.blocks + allocator.blocks_length) { DWORD old_length = allocator.blocks_length; if (!mallocspy_grow(allocator.blocks_length + 0x1000)) return; current = allocator.blocks + old_length; } } *current = mem; allocator.spyed_allocations++; } static void** mallocspy_is_allocation_spyed(const void *mem) { void **current = allocator.blocks; while (*current != mem) { current++; if (current >= allocator.blocks + allocator.blocks_length) return NULL; } return current; } static BOOL mallocspy_remove_spyed_memory(const void *mem) { void **current; if (!allocator.blocks_length) return FALSE; if (!(current = mallocspy_is_allocation_spyed(mem))) return FALSE; allocator.spyed_allocations--; *current = NULL; return TRUE; } static HRESULT WINAPI allocator_QueryInterface(IMalloc *iface, REFIID riid, void **obj) { TRACE("%s, %p.\n", debugstr_guid(riid), obj); if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IMalloc, riid)) { *obj = &allocator.IMalloc_iface; return S_OK; } return E_NOINTERFACE; } static ULONG WINAPI allocator_AddRef(IMalloc *iface) { return 2; } static ULONG WINAPI allocator_Release(IMalloc *iface) { return 1; } static void * WINAPI allocator_Alloc(IMalloc *iface, SIZE_T cb) { void *addr; TRACE("%Id.\n", cb); if (allocator.spy) { SIZE_T preAllocResult; EnterCriticalSection(&allocspy_cs); preAllocResult = IMallocSpy_PreAlloc(allocator.spy, cb); if (cb && !preAllocResult) { /* PreAlloc can force Alloc to fail, but not if cb == 0 */ TRACE("returning null\n"); LeaveCriticalSection(&allocspy_cs); return NULL; } } addr = HeapAlloc(GetProcessHeap(), 0, cb); if (allocator.spy) { addr = IMallocSpy_PostAlloc(allocator.spy, addr); mallocspy_add_mem(addr); LeaveCriticalSection(&allocspy_cs); } TRACE("%p.\n",addr); return addr; } static void * WINAPI allocator_Realloc(IMalloc *iface, void *pv, SIZE_T cb) { void *addr; TRACE("%p, %Id.\n", pv, cb); if (allocator.spy) { void *real_mem; BOOL spyed; EnterCriticalSection(&allocspy_cs); spyed = mallocspy_remove_spyed_memory(pv); cb = IMallocSpy_PreRealloc(allocator.spy, pv, cb, &real_mem, spyed); /* check if can release the spy */ if (allocator.spy_release_pending && !allocator.spyed_allocations) { IMallocSpy_Release(allocator.spy); allocator.spy_release_pending = FALSE; allocator.spy = NULL; LeaveCriticalSection(&allocspy_cs); } if (!cb) { /* PreRealloc can force Realloc to fail */ if (allocator.spy) LeaveCriticalSection(&allocspy_cs); return NULL; } pv = real_mem; } if (!pv) addr = HeapAlloc(GetProcessHeap(), 0, cb); else if (cb) addr = HeapReAlloc(GetProcessHeap(), 0, pv, cb); else { HeapFree(GetProcessHeap(), 0, pv); addr = NULL; } if (allocator.spy) { addr = IMallocSpy_PostRealloc(allocator.spy, addr, TRUE); mallocspy_add_mem(addr); LeaveCriticalSection(&allocspy_cs); } TRACE("%p.\n", addr); return addr; } static void WINAPI allocator_Free(IMalloc *iface, void *mem) { BOOL spyed_block = FALSE, spy_active = FALSE; TRACE("%p.\n", mem); if (!mem) return; if (allocator.spy) { EnterCriticalSection(&allocspy_cs); spyed_block = mallocspy_remove_spyed_memory(mem); spy_active = TRUE; mem = IMallocSpy_PreFree(allocator.spy, mem, spyed_block); } HeapFree(GetProcessHeap(), 0, mem); if (spy_active) { IMallocSpy_PostFree(allocator.spy, spyed_block); /* check if can release the spy */ if (allocator.spy_release_pending && !allocator.spyed_allocations) { IMallocSpy_Release(allocator.spy); allocator.spy_release_pending = FALSE; allocator.spy = NULL; } LeaveCriticalSection(&allocspy_cs); } } /****************************************************************************** * NOTES * FIXME returns: * win95: size allocated (4 byte boundaries) * win2k: size originally requested !!! (allocated on 8 byte boundaries) */ static SIZE_T WINAPI allocator_GetSize(IMalloc *iface, void *mem) { BOOL spyed_block = FALSE, spy_active = FALSE; SIZE_T size; TRACE("%p.\n", mem); if (!mem) return (SIZE_T)-1; if (allocator.spy) { EnterCriticalSection(&allocspy_cs); spyed_block = !!mallocspy_is_allocation_spyed(mem); spy_active = TRUE; mem = IMallocSpy_PreGetSize(allocator.spy, mem, spyed_block); } size = HeapSize(GetProcessHeap(), 0, mem); if (spy_active) { size = IMallocSpy_PostGetSize(allocator.spy, size, spyed_block); LeaveCriticalSection(&allocspy_cs); } return size; } static INT WINAPI allocator_DidAlloc(IMalloc *iface, void *mem) { BOOL spyed_block = FALSE, spy_active = FALSE; int did_alloc; TRACE("%p.\n", mem); if (!mem) return -1; if (allocator.spy) { EnterCriticalSection(&allocspy_cs); spyed_block = !!mallocspy_is_allocation_spyed(mem); spy_active = TRUE; mem = IMallocSpy_PreDidAlloc(allocator.spy, mem, spyed_block); } did_alloc = HeapValidate(GetProcessHeap(), 0, mem); if (spy_active) { did_alloc = IMallocSpy_PostDidAlloc(allocator.spy, mem, spyed_block, did_alloc); LeaveCriticalSection(&allocspy_cs); } return did_alloc; } static void WINAPI allocator_HeapMinimize(IMalloc *iface) { BOOL spy_active = FALSE; TRACE("\n"); if (allocator.spy) { EnterCriticalSection(&allocspy_cs); spy_active = TRUE; IMallocSpy_PreHeapMinimize(allocator.spy); } if (spy_active) { IMallocSpy_PostHeapMinimize(allocator.spy); LeaveCriticalSection(&allocspy_cs); } } static const IMallocVtbl allocator_vtbl = { allocator_QueryInterface, allocator_AddRef, allocator_Release, allocator_Alloc, allocator_Realloc, allocator_Free, allocator_GetSize, allocator_DidAlloc, allocator_HeapMinimize }; /****************************************************************************** * CoGetMalloc (combase.@) */ HRESULT WINAPI CoGetMalloc(DWORD context, IMalloc **imalloc) { if (context != MEMCTX_TASK) { *imalloc = NULL; return E_INVALIDARG; } *imalloc = &allocator.IMalloc_iface; return S_OK; } /*********************************************************************** * CoTaskMemAlloc (combase.@) */ void * WINAPI CoTaskMemAlloc(SIZE_T size) { return IMalloc_Alloc(&allocator.IMalloc_iface, size); } /*********************************************************************** * CoTaskMemFree (combase.@) */ void WINAPI CoTaskMemFree(void *ptr) { IMalloc_Free(&allocator.IMalloc_iface, ptr); } /*********************************************************************** * CoTaskMemRealloc (combase.@) */ void * WINAPI CoTaskMemRealloc(void *ptr, SIZE_T size) { return IMalloc_Realloc(&allocator.IMalloc_iface, ptr, size); } /*********************************************************************** * CoRegisterMallocSpy (combase.@) */ HRESULT WINAPI CoRegisterMallocSpy(IMallocSpy *spy) { HRESULT hr = E_INVALIDARG; TRACE("%p.\n", spy); if (!spy) return E_INVALIDARG; EnterCriticalSection(&allocspy_cs); if (allocator.spy) hr = CO_E_OBJISREG; else if (SUCCEEDED(IMallocSpy_QueryInterface(spy, &IID_IMallocSpy, (void **)&spy))) { allocator.spy = spy; hr = S_OK; } LeaveCriticalSection(&allocspy_cs); return hr; } /*********************************************************************** * CoRevokeMallocSpy (combase.@) */ HRESULT WINAPI CoRevokeMallocSpy(void) { HRESULT hr = S_OK; TRACE("\n"); EnterCriticalSection(&allocspy_cs); if (!allocator.spy) hr = CO_E_OBJNOTREG; else if (allocator.spyed_allocations) { allocator.spy_release_pending = TRUE; hr = E_ACCESSDENIED; } else { IMallocSpy_Release(allocator.spy); allocator.spy = NULL; } LeaveCriticalSection(&allocspy_cs); return hr; }