fpc/rtl/inc/heap.inc
Jonas Maebe 6840bd8ece * changed formatting to conform to the rest of the compiler/rtl
* fixed SysMaxAvail so it also looks at the free fixed size blocks
2004-08-10 18:58:36 +00:00

1426 lines
36 KiB
PHP

{
$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;
MemAvail: @SysMemAvail;
MaxAvail: @SysMaxAvail;
HeapSize: @SysHeapSize;
);
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_memavail : ptrint;
internal_heapsize : ptrint;
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;
function MaxAvail:ptrint;
begin
if IsMultiThread and MemoryManager.NeedLock then
begin
try
MemoryMutexManager.MutexLock;
MaxAvail := MemoryManager.MaxAvail();
finally
MemoryMutexManager.MutexUnlock;
end;
end
else
begin
MaxAvail := MemoryManager.MaxAvail();
end;
end;
function MemAvail:ptrint;
begin
if IsMultiThread and MemoryManager.NeedLock then
begin
try
MemoryMutexManager.MutexLock;
MemAvail := MemoryManager.MemAvail();
finally
MemoryMutexManager.MutexUnlock;
end;
end
else
begin
MemAvail := MemoryManager.MemAvail();
end;
end;
{ FPC Additions }
function HeapSize:ptrint;
begin
if IsMultiThread and MemoryManager.NeedLock then
begin
try
MemoryMutexManager.MutexLock;
HeapSize := MemoryManager.HeapSize();
finally
MemoryMutexManager.MutexUnlock;
end;
end
else
begin
HeapSize := MemoryManager.HeapSize();
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}
{*****************************************************************************
Heapsize,Memavail,MaxAvail
*****************************************************************************}
function SysHeapsize : ptrint;
begin
Sysheapsize := internal_heapsize;
end;
function SysMemavail : ptrint;
begin
Sysmemavail := internal_memavail;
end;
function SysMaxavail: ptrint;
var
pmc : pmemchunk_var;
i: longint;
begin
pmc := freelist_var;
sysmaxavail := 0;
while assigned(pmc) do
begin
if pmc^.size>sysmaxavail then
sysmaxavail := pmc^.size;
pmc := pmc^.next_var;
end;
if sysmaxavail = 0 then
begin
for i := maxblockindex downto 1 do
if assigned(freelists_fixed[i]) then
begin
sysmaxavail := i shl blockshr;
exit;
end;
end;
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_heapsize, poc^.size);
dec(internal_memavail, 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;
i, count : ptrint;
chunksize : ptrint;
begin
{ increase size by size needed for os block header }
size := size + sizeof(toschunk);
{ blocks available in freelist? }
result := freeoslist;
while result <> nil do
begin
if poschunk(result)^.size > size 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+$ffff) and $ffff0000);
DumpBlocks;
{$endif}
{ allocate by 64K size }
size := (size+$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_memavail,size);
inc(internal_heapsize,size);
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 }
chunksize := blockindex shl blockshr;
count := (size-sizeof(toschunk)) div chunksize;
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);
{$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<pbest^.size) then
pbest := pcurr;
end;
end;
{$else BESTMATCH}
if 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;
{$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;
dec(internal_memavail,size);
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);
inc(internal_memavail,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);
inc(internal_memavail,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
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
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;
dec(internal_memavail,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;
{*****************************************************************************
Mark/Release
*****************************************************************************}
procedure release(var p : pointer);
begin
end;
procedure mark(var p : pointer);
begin
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;
internal_heapsize := GetHeapSize;
internal_memavail := internal_heapsize;
end;
{
$Log$
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)
}