//============================================================================ // Copyright (c) 1996, Microsoft Corporation // // File: mm.c // // History: // Abolade Gbadegesin Jan-226-1996 Created. // // Contains code for memory management in IPRIP //============================================================================ #include #include #include #include #include #include "mm.h" // Function: MmHeapCreate // // This function creates a heap and initializes lists which will contain // the addresses of allocated and freed blocks of memory. HANDLE MmHeapCreate( DWORD dwInitialSize, DWORD dwMaximumSize ) { DWORD dwErr; HANDLE hHeap; MMHEAP *pheap; // // create a Win32 heap; we specify no serialization // since our critical section will enforce serialization // hHeap = HeapCreate(HEAP_NO_SERIALIZE, dwInitialSize, dwMaximumSize); if (hHeap == NULL) { return NULL; } // // within the heap created, allocate space for the managed heap // pheap = HeapAlloc(hHeap, 0, sizeof(MMHEAP)); if (pheap == NULL) { dwErr = GetLastError(); HeapDestroy(hHeap); SetLastError(dwErr); return NULL; } // // initialize the managed heap // pheap->hHeap = hHeap; InitializeListHead(&pheap->lhFreeList); InitializeListHead(&pheap->lhBusyList); InitializeCriticalSection(&pheap->csListLock); // // return a pointer to the managed heap structure // return (HANDLE)pheap; } // Function: MmHeapDestroy // // This function destroys a heap. BOOL MmHeapDestroy( HANDLE hHeap ) { MMHEAP *pheap; pheap = (MMHEAP *)hHeap; // // delete the lists' synchronization object // DeleteCriticalSection(&pheap->csListLock); // // a managed heap can be destroyed by merely destroying the heap // which was initially created for the managed heap, since all // allocations came out of that heap // return HeapDestroy(pheap->hHeap); } // Function: MmHeapAlloc // // This function makes an allocation from a managed heap PVOID MmHeapAlloc( HANDLE hHeap, DWORD dwBytes ) { INT cmp; DWORD dwErr; MMHEAP *pheap; MMHEADER *phdr; LIST_ENTRY *ple, *phead; if (!hHeap || !dwBytes) { return NULL; } pheap = (MMHEAP *)hHeap; EnterCriticalSection(&pheap->csListLock); // // search the free-list for the allocation which // is closest in size to the number of bytes requested // phdr = NULL; phead = &pheap->lhFreeList; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { phdr = CONTAINING_RECORD(ple, MMHEADER, leLink); cmp = (dwBytes - phdr->dwBlockSize); if (cmp < 0) { continue; } else if (cmp > 0) { break; } // // the entry found is precisely the required size; // break; } // // if a re-usable entry was found, reset its timestamp, // move it to the busy-list, and return a pointer past the header. // otherwise, allocate a new entry for the caller, // initialize it, place it on the busy-list, and return a pointer. // if (ple != phead) { // // a re-usable entry was found // RemoveEntryList(&phdr->leLink); } else { // // no re-usable entry was found, allocate a new one // phdr = HeapAlloc( pheap->hHeap, HEAP_NO_SERIALIZE, dwBytes + sizeof(MMHEADER) ); if (!phdr) { dwErr = GetLastError(); LeaveCriticalSection(&pheap->csListLock); SetLastError(dwErr); return NULL; } } // // set the entry's timestamp and put it on the busy list // NtQuerySystemTime(&phdr->liTimeStamp); InsertHeadList(&pheap->lhBusyList, &phdr->leLink); LeaveCriticalSection(&pheap->csListLock); return (PVOID)(phdr + 1); } // Function: MmHeapFree // // This function frees an allocation made by MmHeapAlloc BOOL MmHeapFree( HANDLE hHeap, PVOID pMem ) { INT cmp; MMHEAP *pheap; MMHEADER *phdr, *phdrFree; LIST_ENTRY *ple, *phead; if (!hHeap || !pMem) { return FALSE; } pheap = (MMHEAP *)hHeap; EnterCriticalSection(&pheap->csListLock); phdr = (MMHEADER *)((PBYTE)pMem - sizeof(MMHEADER)); // // remove the entry from the busy-list // RemoveEntryList(&phdr->leLink); // // place the entry on the free-list, // which is in order of ascending size (smallest-first) // phead = &pheap->lhFreeList; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { phdrFree = CONTAINING_RECORD(ple, MMHEADER, leLink); cmp = (phdr->dwBlockSize - phdrFree->dwBlockSize); if (cmp < 0) { break; } else if (cmp > 0) { continue; } // // the allocations are the same size, but the newly-free one // is most likely the most-recently active // break; } // // insert the newly-free entry before the one found // (since this is a circular list, we accomplish this // using InsertTailList; think about it a while.) // NtQuerySystemTime(&phdr->liTimeStamp); InsertTailList(ple, &phdr->leLink); LeaveCriticalSection(&pheap->csListLock); return TRUE; } // Function: MmHeapCollectGarbage // // This function is called by the owner of the heap. // It takes as its argument an interval I in systime-units // (system-time is measured in 100-nanosecond units), and any allocations // on the free list which have not been re-used in the past I systime-units // are returned to the Win32 heap. // // To return all free entries to the Win32 heap, specify an interval of 0. BOOL MmHeapCollectGarbage( HANDLE hHeap, LARGE_INTEGER liInterval ) { INT cmp; MMHEAP *pheap; MMHEADER *phdr; LIST_ENTRY *ple, *phead; LARGE_INTEGER liCutoff; if (!hHeap) { return FALSE; } pheap = (MMHEAP *)hHeap; EnterCriticalSection(&pheap->csListLock); // // get the current system-time, and from that compute the cutoff-time // for allocation timestamps, by subtracting the interval passed in // to get the time of the earliest allocation which can be exempt // from garbage-collection // NtQuerySystemTime(&liCutoff); liCutoff = RtlLargeIntegerSubtract(liCutoff, liInterval); // // go through the free-list // phead = &pheap->lhFreeList; for (ple = phead->Flink; ple != phead; ple = ple->Flink) { phdr = CONTAINING_RECORD(ple, MMHEADER, leLink); if (RtlLargeIntegerLessThan(liCutoff, phdr->liTimeStamp)) { continue; } // // this allocation is stamped from before the cutoff interval, // so we'll have to free it (with care, since it is a link // in the list we are walking through). // ple = ple->Blink; RemoveEntryList(&phdr->leLink); HeapFree(pheap->hHeap, HEAP_NO_SERIALIZE, phdr); } LeaveCriticalSection(&pheap->csListLock); return TRUE; }