{ $Id$ This file is part of the Free Pascal run time library. Copyright (c) 1999-2000 by the Free Pascal development team. functions for heap management in the data segment See the file COPYING.FPC, included in this distribution, for details about the copyright. This program 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. **********************************************************************} {****************************************************************************} { Try to find the best matching block in general freelist } { define BESTMATCH} { DEBUG: Dump info when the heap needs to grow } { define DUMPGROW} { DEBUG: Test the FreeList on correctness } {$ifdef SYSTEMDEBUG} {$define TestFreeLists} {$endif SYSTEMDEBUG} const {$ifdef CPU64} blocksize = 32; { at least size of freerecord } blockshr = 5; { shr value for blocksize=2^blockshr} maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord } {$else} blocksize = 16; { at least size of freerecord } blockshr = 4; { shr value for blocksize=2^blockshr} maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord } {$endif} maxblockindex = maxblocksize div blocksize; { highest index in array of lists of memchunks } maxreusebigger = 8; { max reuse bigger tries } usedflag = 1; { flag if the block is used or not } lastblockflag = 2; { flag if the block is the last in os chunk } firstblockflag = 4; { flag if the block is the first in os chunk } fixedsizeflag = 8; { flag if the block is of fixed size } sizemask = not(blocksize-1); fixedsizemask = sizemask and $ffff; {****************************************************************************} {$ifdef DUMPGROW} {$define DUMPBLOCKS} {$endif} { Forward defines } procedure SysHeapMutexInit;forward; procedure SysHeapMutexDone;forward; procedure SysHeapMutexLock;forward; procedure SysHeapMutexUnlock;forward; { Memory manager } const MemoryManager: TMemoryManager = ( NeedLock: true; GetMem: @SysGetMem; FreeMem: @SysFreeMem; FreeMemSize: @SysFreeMemSize; AllocMem: @SysAllocMem; ReAllocMem: @SysReAllocMem; MemSize: @SysMemSize; GetHeapStatus: @SysGetHeapStatus; ); MemoryMutexManager: TMemoryMutexManager = ( MutexInit: @SysHeapMutexInit; MutexDone: @SysHeapMutexDone; MutexLock: @SysHeapMutexLock; MutexUnlock: @SysHeapMutexUnlock; ); type pmemchunk_fixed = ^tmemchunk_fixed; tmemchunk_fixed = record {$ifdef cpusparc} { Sparc needs to alloc aligned on 8 bytes, to allow doubles } _dummy : ptrint; {$endif cpusparc} size : ptrint; next_fixed, prev_fixed : pmemchunk_fixed; end; pmemchunk_var = ^tmemchunk_var; tmemchunk_var = record prevsize : ptrint; size : ptrint; next_var, prev_var : pmemchunk_var; end; { ``header'', ie. size of structure valid when chunk is in use } { should correspond to tmemchunk_var_hdr structure starting with the last field. Reason is that the overlap is starting from the end of the record. } tmemchunk_fixed_hdr = record {$ifdef cpusparc} { Sparc needs to alloc aligned on 8 bytes, to allow doubles } _dummy : ptrint; {$endif cpusparc} size : ptrint; end; tmemchunk_var_hdr = record prevsize : ptrint; size : ptrint; end; poschunk = ^toschunk; toschunk = record size : ptrint; next, prev : poschunk; used : ptrint; end; tfreelists = array[1..maxblockindex] of pmemchunk_fixed; pfreelists = ^tfreelists; var internal_status : THeapStatus; freelists_fixed : tfreelists; freelist_var : pmemchunk_var; freeoslist : poschunk; freeoslistcount : dword; {$ifdef TestFreeLists} { this can be turned on by debugger } const test_each : boolean = false; {$endif TestFreeLists} {***************************************************************************** Memory Manager *****************************************************************************} procedure SetMemoryMutexManager(var MutexMgr: TMemoryMutexManager); begin { Release old mutexmanager, the default manager does nothing so calling this without initializing is safe } MemoryMutexManager.MutexDone; { Copy new mutexmanager } MemoryMutexManager := MutexMgr; { Init new mutexmanager } MemoryMutexManager.MutexInit; end; procedure GetMemoryManager(var MemMgr:TMemoryManager); begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; MemMgr := MemoryManager; finally MemoryMutexManager.MutexUnlock; end; end else begin MemMgr := MemoryManager; end; end; procedure SetMemoryManager(const MemMgr:TMemoryManager); begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; MemoryManager := MemMgr; finally MemoryMutexManager.MutexUnlock; end; end else begin MemoryManager := MemMgr; end; end; function IsMemoryManagerSet:Boolean; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or (MemoryManager.FreeMem<>@SysFreeMem); finally MemoryMutexManager.MutexUnlock; end; end else begin IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or (MemoryManager.FreeMem<>@SysFreeMem); end; end; procedure GetMem(Var p:pointer;Size:ptrint); begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; p := MemoryManager.GetMem(Size); finally MemoryMutexManager.MutexUnlock; end; end else begin p := MemoryManager.GetMem(Size); end; end; procedure GetMemory(Var p:pointer;Size:ptrint); begin GetMem(p,size); end; procedure FreeMem(p:pointer;Size:ptrint); begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; MemoryManager.FreeMemSize(p,Size); finally MemoryMutexManager.MutexUnlock; end; end else begin MemoryManager.FreeMemSize(p,Size); end; end; procedure FreeMemory(p:pointer;Size:ptrint); begin FreeMem(p,size); end; procedure GetHeapStatus(var status:THeapStatus); begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; MemoryManager.GetHeapStatus(status); finally MemoryMutexManager.MutexUnlock; end; end else begin MemoryManager.GetHeapStatus(status); end; end; function MemSize(p:pointer):ptrint; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; MemSize := MemoryManager.MemSize(p); finally MemoryMutexManager.MutexUnlock; end; end else begin MemSize := MemoryManager.MemSize(p); end; end; { Delphi style } function FreeMem(p:pointer):ptrint; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; Freemem := MemoryManager.FreeMem(p); finally MemoryMutexManager.MutexUnlock; end; end else begin Freemem := MemoryManager.FreeMem(p); end; end; function FreeMemory(p:pointer):ptrint; begin FreeMemory := FreeMem(p); end; function GetMem(size:ptrint):pointer; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; GetMem := MemoryManager.GetMem(Size); finally MemoryMutexManager.MutexUnlock; end; end else begin GetMem := MemoryManager.GetMem(Size); end; end; function GetMemory(size:ptrint):pointer; begin GetMemory := Getmem(size); end; function AllocMem(Size:ptrint):pointer; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; AllocMem := MemoryManager.AllocMem(size); finally MemoryMutexManager.MutexUnlock; end; end else begin AllocMem := MemoryManager.AllocMem(size); end; end; function ReAllocMem(var p:pointer;Size:ptrint):pointer; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; ReAllocMem := MemoryManager.ReAllocMem(p,size); finally MemoryMutexManager.MutexUnlock; end; end else begin ReAllocMem := MemoryManager.ReAllocMem(p,size); end; end; function ReAllocMemory(var p:pointer;Size:ptrint):pointer; begin ReAllocMemory := ReAllocMem(p,size); end; {$ifdef ValueGetmem} { Needed for calls from Assembler } function fpc_getmem(size:ptrint):pointer;compilerproc;[public,alias:'FPC_GETMEM']; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; fpc_GetMem := MemoryManager.GetMem(size); finally MemoryMutexManager.MutexUnlock; end; end else begin fpc_GetMem := MemoryManager.GetMem(size); end; end; {$else ValueGetmem} { Needed for calls from Assembler } procedure AsmGetMem(var p:pointer;size:ptrint);[public,alias:'FPC_GETMEM']; begin p := MemoryManager.GetMem(size); end; {$endif ValueGetmem} {$ifdef ValueFreemem} procedure fpc_freemem(p:pointer);compilerproc;[public,alias:'FPC_FREEMEM']; begin if IsMultiThread and MemoryManager.NeedLock then begin try MemoryMutexManager.MutexLock; if p <> nil then MemoryManager.FreeMem(p); finally MemoryMutexManager.MutexUnlock; end; end else begin if p <> nil then MemoryManager.FreeMem(p); end; end; {$else ValueFreemem} procedure AsmFreeMem(var p:pointer);[public,alias:'FPC_FREEMEM']; begin if p <> nil then MemoryManager.FreeMem(p); end; {$endif ValueFreemem} { Bootstrapping } {$ifndef HASGETHEAPSTATUS} Function Memavail:ptrint; begin result:=0; end; Function Maxavail:ptrint; begin result:=0; end; Function Heapsize:ptrint; begin result:=0; end; {$endif HASGETHEAPSTATUS} {***************************************************************************** GetHeapStatus *****************************************************************************} procedure SysGetHeapStatus(var status:THeapStatus); begin internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed; status:=internal_status; end; {$ifdef DUMPBLOCKS} // TODO procedure DumpBlocks; var s,i,j : ptrint; hp : pfreerecord; begin for i := 1 to maxblock do begin hp := freelists[i]; j := 0; while assigned(hp) do begin inc(j); hp := hp^.next; end; writeln('Block ',i*blocksize,': ',j); end; { freelist 0 } hp := freelists[0]; j := 0; s := 0; while assigned(hp) do begin inc(j); if hp^.size>s then s := hp^.size; hp := hp^.next; end; writeln('Main: ',j,' maxsize: ',s); end; {$endif} {$ifdef TestFreeLists} procedure TestFreeLists; var i,j : ptrint; mc : pmemchunk_fixed; begin for i := 1 to maxblockindex do begin j := 0; mc := freelists_fixed[i]; while assigned(mc) do begin inc(j); if ((mc^.size and fixedsizemask) <> i * blocksize) then RunError(204); mc := mc^.next_fixed; end; end; end; {$endif TestFreeLists} {***************************************************************************** List adding/removal *****************************************************************************} procedure append_to_list_fixed(blockindex: ptrint; pmc: pmemchunk_fixed); begin pmc^.prev_fixed := nil; pmc^.next_fixed := freelists_fixed[blockindex]; if freelists_fixed[blockindex]<>nil then freelists_fixed[blockindex]^.prev_fixed := pmc; freelists_fixed[blockindex] := pmc; end; procedure append_to_list_var(pmc: pmemchunk_var); begin pmc^.prev_var := nil; pmc^.next_var := freelist_var; if freelist_var<>nil then freelist_var^.prev_var := pmc; freelist_var := pmc; end; procedure remove_from_list_fixed(blockindex: ptrint; pmc: pmemchunk_fixed); begin if assigned(pmc^.next_fixed) then pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed; if assigned(pmc^.prev_fixed) then pmc^.prev_fixed^.next_fixed := pmc^.next_fixed else freelists_fixed[blockindex] := pmc^.next_fixed; end; procedure remove_from_list_var(pmc: pmemchunk_var); begin if assigned(pmc^.next_var) then pmc^.next_var^.prev_var := pmc^.prev_var; if assigned(pmc^.prev_var) then pmc^.prev_var^.next_var := pmc^.next_var else freelist_var := pmc^.next_var; end; procedure append_to_oslist(poc: poschunk); begin { decide whether to free block or add to list } {$ifdef HAS_SYSOSFREE} if freeoslistcount >= 3 then begin dec(internal_status.currheapsize, poc^.size); SysOSFree(poc, poc^.size); end else begin {$endif} poc^.prev := nil; poc^.next := freeoslist; if freeoslist <> nil then freeoslist^.prev := poc; freeoslist := poc; inc(freeoslistcount); {$ifdef HAS_SYSOSFREE} end; {$endif} end; procedure remove_from_oslist(poc: poschunk); begin if assigned(poc^.next) then poc^.next^.prev := poc^.prev; if assigned(poc^.prev) then poc^.prev^.next := poc^.next else freeoslist := poc^.next; dec(freeoslistcount); end; procedure append_to_oslist_var(pmc: pmemchunk_var); var poc: poschunk; begin // block eligable for freeing poc := pointer(pmc)-sizeof(toschunk); remove_from_list_var(pmc); append_to_oslist(poc); end; procedure append_to_oslist_fixed(blockindex, chunksize: ptrint; poc: poschunk); var pmc: pmemchunk_fixed; i, count: ptrint; begin count := (poc^.size - sizeof(toschunk)) div chunksize; pmc := pmemchunk_fixed(pointer(poc)+sizeof(toschunk)); for i := 0 to count - 1 do begin remove_from_list_fixed(blockindex, pmc); pmc := pointer(pmc)+chunksize; end; append_to_oslist(poc); end; {***************************************************************************** Split block *****************************************************************************} procedure split_block(pcurr: pmemchunk_var; size: ptrint); var pcurr_tmp : pmemchunk_var; sizeleft: ptrint; begin sizeleft := (pcurr^.size and sizemask)-size; if sizeleft>=blocksize then begin pcurr_tmp := pmemchunk_var(pointer(pcurr)+size); { update prevsize of block to the right } if (pcurr^.size and lastblockflag) = 0 then pmemchunk_var(pointer(pcurr)+(pcurr^.size and sizemask))^.prevsize := sizeleft; { inherit the lastblockflag } pcurr_tmp^.size := sizeleft or (pcurr^.size and lastblockflag); pcurr_tmp^.prevsize := size; { the block we return is not the last one anymore (there's now a block after it) } { decrease size of block to new size } pcurr^.size := size or (pcurr^.size and (not sizemask and not lastblockflag)); { insert the block in the freelist } append_to_list_var(pcurr_tmp); end; end; {***************************************************************************** Try concat freerecords *****************************************************************************} procedure concat_two_blocks(mc_left, mc_right: pmemchunk_var); var mc_tmp : pmemchunk_var; size_right : ptrint; begin // left block free, concat with right-block size_right := mc_right^.size and sizemask; inc(mc_left^.size, size_right); // if right-block was last block, copy flag if (mc_right^.size and lastblockflag) <> 0 then begin mc_left^.size := mc_left^.size or lastblockflag; end else begin // there is a block to the right of the right-block, adjust it's prevsize mc_tmp := pmemchunk_var(pointer(mc_right)+size_right); mc_tmp^.prevsize := mc_left^.size and sizemask; end; // remove right-block from doubly linked list remove_from_list_var(mc_right); end; procedure try_concat_free_chunk_forward(mc: pmemchunk_var); var mc_tmp : pmemchunk_var; begin { try concat forward } if (mc^.size and lastblockflag) = 0 then begin mc_tmp := pmemchunk_var(pointer(mc)+(mc^.size and sizemask)); if (mc_tmp^.size and usedflag) = 0 then begin // next block free: concat concat_two_blocks(mc, mc_tmp); end; end; end; function try_concat_free_chunk(mc: pmemchunk_var): pmemchunk_var; var mc_tmp : pmemchunk_var; begin try_concat_free_chunk_forward(mc); { try concat backward } if (mc^.size and firstblockflag) = 0 then begin mc_tmp := pmemchunk_var(pointer(mc)-mc^.prevsize); if (mc_tmp^.size and usedflag) = 0 then begin // prior block free: concat concat_two_blocks(mc_tmp, mc); mc := mc_tmp; end; end; result := mc; end; {***************************************************************************** Grow Heap *****************************************************************************} function alloc_oschunk(blockindex, size: ptrint): pointer; var pmc : pmemchunk_fixed; pmcv : pmemchunk_var; minsize, maxsize, i, count : ptrint; chunksize : ptrint; begin { increase size by size needed for os block header } minsize := size + sizeof(toschunk); if blockindex<>0 then maxsize := (size * $ffff) + sizeof(toschunk) else maxsize := high(ptrint); { blocks available in freelist? } result := freeoslist; while result <> nil do begin if (poschunk(result)^.size >= minsize) and (poschunk(result)^.size <= maxsize) then begin size := poschunk(result)^.size; remove_from_oslist(poschunk(result)); break; end; result := poschunk(result)^.next; end; if result = nil then begin {$ifdef DUMPGROW} writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and $ffff0000); DumpBlocks; {$endif} { allocate by 64K size } size := (size+sizeof(toschunk)+$ffff) and not $ffff; { allocate smaller blocks for fixed-size chunks } if blockindex<>0 then begin result := SysOSAlloc(GrowHeapSizeSmall); if result<>nil then size := GrowHeapSizeSmall; end { first try 256K (default) } else if size<=GrowHeapSize1 then begin result := SysOSAlloc(GrowHeapSize1); if result<>nil then size := GrowHeapSize1; end { second try 1024K (default) } else if size<=GrowHeapSize2 then begin result := SysOSAlloc(GrowHeapSize2); if result<>nil then size := GrowHeapSize2; end { else allocate the needed bytes } else result := SysOSAlloc(size); { try again } if result=nil then begin result := SysOSAlloc(size); if (result=nil) then begin if ReturnNilIfGrowHeapFails then exit else HandleError(203); end; end; { set the total new heap size } inc(internal_status.currheapsize,size); if internal_status.currheapsize>internal_status.maxheapsize then internal_status.maxheapsize:=internal_status.currheapsize; end; { initialize os-block } poschunk(result)^.used := 0; poschunk(result)^.size := size; inc(result, sizeof(toschunk)); if blockindex<>0 then begin { chop os chunk in fixedsize parts, maximum of $ffff elements are allowed, otherwise there will be an overflow } chunksize := blockindex shl blockshr; count := (size-sizeof(toschunk)) div chunksize; if count>$ffff then HandleError(204); pmc := pmemchunk_fixed(result); pmc^.prev_fixed := nil; i := 0; repeat pmc^.size := fixedsizeflag or chunksize or (i shl 16); pmc^.next_fixed := pointer(pmc)+chunksize; inc(i); if i < count then begin pmc := pmemchunk_fixed(pointer(pmc)+chunksize); pmc^.prev_fixed := pointer(pmc)-chunksize; end else begin break; end; until false; append_to_list_fixed(blockindex, pmc); pmc^.prev_fixed := pointer(pmc)-chunksize; freelists_fixed[blockindex] := pmemchunk_fixed(result); end else begin pmcv := pmemchunk_var(result); append_to_list_var(pmcv); pmcv^.size := ((size-sizeof(toschunk)) and sizemask) or (firstblockflag or lastblockflag); pmcv^.prevsize := 0; end; {$ifdef TestFreeLists} TestFreeLists; {$endif TestFreeLists} end; {***************************************************************************** SysGetMem *****************************************************************************} function SysGetMem_Fixed(size: ptrint): pointer; var pcurr: pmemchunk_fixed; poc: poschunk; s: ptrint; begin result:=nil; { try to find a block in one of the freelists per size } s := size shr blockshr; pcurr := freelists_fixed[s]; { no free blocks ? } if not assigned(pcurr) then begin pcurr := alloc_oschunk(s, size); if not assigned(pcurr) then exit; end; { get a pointer to the block we should return } result := pointer(pcurr)+sizeof(tmemchunk_fixed_hdr); { flag as in-use } pcurr^.size := pcurr^.size or usedflag; { update freelist } freelists_fixed[s] := pcurr^.next_fixed; if assigned(freelists_fixed[s]) then freelists_fixed[s]^.prev_fixed := nil; poc := poschunk(pointer(pcurr)-((pcurr^.size shr 16)*(pcurr^.size and fixedsizemask)+sizeof(toschunk))); inc(poc^.used); { statistics } inc(internal_status.currheapused,size); if internal_status.currheapused>internal_status.maxheapused then internal_status.maxheapused:=internal_status.currheapused; {$ifdef TestFreeLists} if test_each then TestFreeLists; {$endif TestFreeLists} end; function SysGetMem_Var(size: ptrint): pointer; var pcurr, pcurr_tmp : pmemchunk_var; {$ifdef BESTMATCH} pbest : pmemchunk_var; {$endif} begin result:=nil; {$ifdef BESTMATCH} pbest := nil; {$endif} pcurr := freelist_var; while assigned(pcurr) do begin {$ifdef BESTMATCH} if pcurr^.size=size then begin break; end else begin if (pcurr^.size>size) then begin if (not assigned(pbest)) or (pcurr^.size=size then break; {$endif BESTMATCH} pcurr := pcurr^.next_var; end; {$ifdef BESTMATCH} if not assigned(pcurr) then pcurr := pbest; {$endif} if not assigned(pcurr) then begin // all os-chunks full, allocate a new one pcurr := alloc_oschunk(0, size); if not assigned(pcurr) then exit; end; { get pointer of the block we should return } result := pointer(pcurr)+sizeof(tmemchunk_var_hdr); { remove the current block from the freelist } remove_from_list_var(pcurr); { create the left over freelist block, if at least 16 bytes are free } split_block(pcurr, size); { flag block as used } pcurr^.size := pcurr^.size or usedflag; { statistics } inc(internal_status.currheapused,size); if internal_status.currheapused>internal_status.maxheapused then internal_status.maxheapused:=internal_status.currheapused; {$ifdef TestFreeLists} if test_each then TestFreeLists; {$endif TestFreeLists} end; function SysGetMem(size : ptrint):pointer; begin { Something to allocate ? } if size<=0 then begin { give an error for < 0 } if size<0 then HandleError(204); { we always need to allocate something, using heapend is not possible, because heappend can be changed by growheap (PFV) } size := 1; end; { calc to multiple of 16 after adding the needed bytes for memchunk header } if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then begin size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask; sysgetmem := sysgetmem_fixed(size); end else begin size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask; sysgetmem := sysgetmem_var(size); end; end; {***************************************************************************** SysFreeMem *****************************************************************************} function SysFreeMem_Fixed(pcurr: pmemchunk_fixed; size: ptrint): ptrint; var pcurrsize: ptrint; blockindex: ptrint; poc: poschunk; begin pcurrsize := pcurr^.size and fixedsizemask; if size<>pcurrsize then HandleError(204); dec(internal_status.currheapused,pcurrsize); { insert the block in it's freelist } pcurr^.size := pcurr^.size and (not usedflag); blockindex := pcurrsize shr blockshr; append_to_list_fixed(blockindex, pcurr); { decrease used blocks count } poc := poschunk(pointer(pcurr)-(pcurr^.size shr 16)*pcurrsize-sizeof(toschunk)); if poc^.used = 0 then HandleError(204); dec(poc^.used); if poc^.used = 0 then begin // block eligable for freeing append_to_oslist_fixed(blockindex, pcurrsize, poc); end; SysFreeMem_Fixed := pcurrsize; {$ifdef TestFreeLists} if test_each then TestFreeLists; {$endif TestFreeLists} end; function SysFreeMem_Var(pcurr: pmemchunk_var; size: ptrint): ptrint; var pcurrsize: ptrint; begin pcurrsize := pcurr^.size and sizemask; if size<>pcurrsize then HandleError(204); dec(internal_status.currheapused,pcurrsize); { insert the block in it's freelist } pcurr^.size := pcurr^.size and (not usedflag); append_to_list_var(pcurr); SysFreeMem_Var := pcurrsize; pcurr := try_concat_free_chunk(pcurr); if (pcurr^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then begin append_to_oslist_var(pcurr); end; {$ifdef TestFreeLists} if test_each then TestFreeLists; {$endif TestFreeLists} end; function SysFreeMem(p: pointer): ptrint; var pcurrsize: ptrint; begin if p=nil then exit; pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size; { check if this is a fixed- or var-sized chunk } if (pcurrsize and fixedsizeflag) = 0 then begin result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)), pcurrsize and sizemask); end else begin result := sysfreemem_fixed(pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr)), pcurrsize and fixedsizemask); end; end; {***************************************************************************** SysFreeMemSize *****************************************************************************} Function SysFreeMemSize(p: pointer; size: ptrint):ptrint; var pcurrsize: ptrint; begin SysFreeMemSize := 0; if size<=0 then begin if size<0 then HandleError(204); exit; end; if p=nil then HandleError(204); pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size; { check if this is a fixed- or var-sized chunk } if (pcurrsize and fixedsizeflag) = 0 then begin size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask; result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)), size); end else begin size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask; result := sysfreemem_fixed(pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr)), size); end; end; {***************************************************************************** SysMemSize *****************************************************************************} function SysMemSize(p: pointer): ptrint; begin SysMemSize := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size; if (SysMemSize and fixedsizeflag) = 0 then begin SysMemSize := SysMemSize and sizemask; dec(SysMemSize, sizeof(tmemchunk_var_hdr)); end else begin SysMemSize := SysMemSize and fixedsizemask; dec(SysMemSize, sizeof(tmemchunk_fixed_hdr)); end; end; {***************************************************************************** SysAllocMem *****************************************************************************} function SysAllocMem(size: ptrint): pointer; begin sysallocmem := MemoryManager.GetMem(size); if sysallocmem<>nil then FillChar(sysallocmem^,MemoryManager.MemSize(sysallocmem),0); end; {***************************************************************************** SysResizeMem *****************************************************************************} function SysTryResizeMem(var p: pointer; size: ptrint): boolean; var pcurrsize, oldsize, currsize, sizeleft : ptrint; pnew, pcurr : pmemchunk_var; begin { fix needed size } if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then begin size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask; end else begin size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask; end; { fix p to point to the heaprecord } pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size; if (pcurrsize and fixedsizeflag) = 0 then begin currsize := pcurrsize and sizemask; end else begin currsize := pcurrsize and fixedsizemask; end; oldsize := currsize; { is the allocated block still correct? } if (currsize>=size) and (size>(currsize-16)) then begin SysTryResizeMem := true; {$ifdef TestFreeLists} if test_each then TestFreeLists; {$endif TestFreeLists} exit; end; { don't do resizes on fixed-size blocks } // if (pcurrsize and fixedsizeflag) <> 0 then // begin SysTryResizeMem := false; exit; // end; { get pointer to block } pcurr := pmemchunk_var(pointer(p)-sizeof(tmemchunk_var_hdr)); { do we need to allocate more memory ? } if size>currsize then begin { the size is bigger than the previous size, we need to allocated more mem. We first check if the blocks after the current block are free. If not we simply call getmem/freemem to get the new block } try_concat_free_chunk_forward(pcurr); currsize := (pcurr^.size and sizemask); SysTryResizeMem := currsize>=size; end; if currsize>size then begin { is the size smaller then we can adjust the block to that size and insert the other part into the freelist } { create the left over freelist block, if at least 16 bytes are free } split_block(pcurr, size); SysTryResizeMem := true; end; inc(internal_status.currheapused,size-oldsize); {$ifdef TestFreeLists} if test_each then TestFreeLists; {$endif TestFreeLists} end; {***************************************************************************** SysResizeMem *****************************************************************************} function SysReAllocMem(var p: pointer; size: ptrint):pointer; var minsize : ptrint; p2 : pointer; begin { Free block? } if size=0 then begin if p<>nil then begin MemoryManager.FreeMem(p); p := nil; end; end else { Allocate a new block? } if p=nil then begin p := MemoryManager.AllocMem(size); end else { Resize block } if not SysTryResizeMem(p,size) then begin minsize := MemoryManager.MemSize(p); if size < minsize then minsize := size; p2 := MemoryManager.AllocMem(size); if p2<>nil then Move(p^,p2^,minsize); MemoryManager.FreeMem(p); p := p2; end; SysReAllocMem := p; end; {***************************************************************************** MemoryMutexManager default hooks *****************************************************************************} procedure SysHeapMutexInit; begin { nothing todo } end; procedure SysHeapMutexDone; begin { nothing todo } end; procedure SysHeapMutexLock; begin { give an runtime error. the program is running multithreaded without any heap protection. this will result in unpredictable errors so stopping here with an error is more safe (PFV) } runerror(244); end; procedure SysHeapMutexUnLock; begin { see SysHeapMutexLock for comment } runerror(244); end; {***************************************************************************** InitHeap *****************************************************************************} { This function will initialize the Heap manager and need to be called from the initialization of the system unit } procedure InitHeap; begin FillChar(freelists_fixed,sizeof(tfreelists),0); freelist_var := nil; freeoslist := nil; freeoslistcount := 0; fillchar(internal_status,sizeof(internal_status),0); end; { $Log$ Revision 1.42 2005-01-30 11:56:29 peter * allow Freemem(nil) Revision 1.41 2004/12/19 13:45:56 peter * fixed overflow when reusing a memory block for fixed size chunks Revision 1.40 2004/11/26 22:22:58 peter * fix currheapused Revision 1.39 2004/11/22 22:26:21 peter * typo for GetHeapStatus Revision 1.38 2004/11/22 19:34:58 peter * GetHeapStatus added, removed MaxAvail,MemAvail,HeapSize Revision 1.37 2004/10/25 15:38:59 peter * compiler defined HEAP and HEAPSIZE removed Revision 1.36 2004/08/10 18:58:36 jonas * changed formatting to conform to the rest of the compiler/rtl * fixed SysMaxAvail so it also looks at the free fixed size blocks Revision 1.35 2004/06/29 20:50:32 peter * readded support for ReturnIfGrowHeapFails Revision 1.34 2004/06/27 19:47:27 florian * fixed heap corruption on sparc Revision 1.33 2004/06/27 11:57:18 florian * finally (hopefully) fixed sysalloc trouble Revision 1.32 2004/06/18 14:40:55 peter * moved padding for sparc Revision 1.31 2004/06/17 16:16:13 peter * New heapmanager that releases memory back to the OS, donated by Micha Nelissen Revision 1.30 2004/05/31 12:18:16 peter * sparc needs alignment on 8 bytes to allow doubles Revision 1.29 2004/04/26 16:20:54 peter * 64bit fixes Revision 1.28 2004/03/15 21:48:26 peter * cmem moved to rtl * longint replaced with ptrint in heapmanagers Revision 1.27 2004/03/15 20:42:39 peter * exit with rte 204 instead of looping infinite when a heap record size is overwritten with 0 Revision 1.26 2004/01/29 22:45:25 jonas * improved beforeheapend inheritance (remove flag again when possible, sometimes resulting in more opportunities for try_concat_free_chunk) Revision 1.25 2003/12/15 21:39:16 daniel * Small microoptimization Revision 1.24 2003/10/02 14:03:24 marco * *memORY overloads Revision 1.23 2003/09/28 12:43:48 peter * fixed wrong check when allocation of a block > 1mb failed Revision 1.22 2003/09/27 11:52:35 peter * sbrk returns pointer Revision 1.21 2003/05/23 14:53:48 peter * check newpos < 0 instead of = -1 Revision 1.20 2003/05/01 08:05:23 florian * started to make the rtl 64 bit save by introducing SizeInt and SizeUInt (similar to size_t of C) Revision 1.19 2002/11/01 17:38:04 peter * fix setmemorymutexmanager to call mutexdone on the already installed manager instead of the passed manager Revision 1.18 2002/10/30 20:39:13 peter * MemoryManager record has a field NeedLock if the wrapper functions need to provide locking for multithreaded programs Revision 1.17 2002/10/30 19:54:19 peter * remove wrong lock from SysMemSize, MemSize() does the locking already. Revision 1.16 2002/10/14 19:39:17 peter * threads unit added for thread support Revision 1.15 2002/09/07 15:07:45 peter * old logs removed and tabs fixed Revision 1.14 2002/06/17 08:33:04 jonas * heap manager now fragments the heap much less Revision 1.13 2002/04/21 18:56:59 peter * fpc_freemem and fpc_getmem compilerproc Revision 1.12 2002/02/10 15:33:45 carl * fixed some missing IsMultiThreaded variables Revision 1.11 2002/01/02 13:43:09 jonas * fix for web bug 1727 from Peter (corrected) }