heap.inc 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by the Free Pascal development team.
  5. functions for heap management in the data segment
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {****************************************************************************}
  13. { Try to find the best matching block in general freelist }
  14. { define BESTMATCH}
  15. { DEBUG: Dump info when the heap needs to grow }
  16. { define DUMPGROW}
  17. { DEBUG: Test the FreeList on correctness }
  18. {$ifdef SYSTEMDEBUG}
  19. {$define TestFreeLists}
  20. {$endif SYSTEMDEBUG}
  21. const
  22. {$ifdef CPU64}
  23. blocksize = 32; { at least size of freerecord }
  24. blockshr = 5; { shr value for blocksize=2^blockshr}
  25. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  26. {$else}
  27. blocksize = 16; { at least size of freerecord }
  28. blockshr = 4; { shr value for blocksize=2^blockshr}
  29. maxblocksize = 512+blocksize; { 1024+8 needed for heaprecord }
  30. {$endif}
  31. maxblockindex = maxblocksize div blocksize; { highest index in array of lists of memchunks }
  32. maxreusebigger = 8; { max reuse bigger tries }
  33. usedflag = 1; { flag if the block is used or not }
  34. lastblockflag = 2; { flag if the block is the last in os chunk }
  35. firstblockflag = 4; { flag if the block is the first in os chunk }
  36. fixedsizeflag = 8; { flag if the block is of fixed size }
  37. sizemask = not(blocksize-1);
  38. fixedsizemask = sizemask and $ffff;
  39. {****************************************************************************}
  40. {$ifdef DUMPGROW}
  41. {$define DUMPBLOCKS}
  42. {$endif}
  43. { Forward defines }
  44. procedure SysHeapMutexInit;forward;
  45. procedure SysHeapMutexDone;forward;
  46. procedure SysHeapMutexLock;forward;
  47. procedure SysHeapMutexUnlock;forward;
  48. { Memory manager }
  49. const
  50. MemoryManager: TMemoryManager = (
  51. NeedLock: true;
  52. GetMem: @SysGetMem;
  53. FreeMem: @SysFreeMem;
  54. FreeMemSize: @SysFreeMemSize;
  55. AllocMem: @SysAllocMem;
  56. ReAllocMem: @SysReAllocMem;
  57. MemSize: @SysMemSize;
  58. GetHeapStatus: @SysGetHeapStatus;
  59. {$ifdef HASGETFPCHEAPSTATUS}
  60. GetFPCHeapStatus: @SysGetFPCHeapStatus;
  61. {$endif HASGETFPCHEAPSTATUS}
  62. );
  63. MemoryMutexManager: TMemoryMutexManager = (
  64. MutexInit: @SysHeapMutexInit;
  65. MutexDone: @SysHeapMutexDone;
  66. MutexLock: @SysHeapMutexLock;
  67. MutexUnlock: @SysHeapMutexUnlock;
  68. );
  69. type
  70. pmemchunk_fixed = ^tmemchunk_fixed;
  71. tmemchunk_fixed = record
  72. {$ifdef cpusparc}
  73. { Sparc needs to alloc aligned on 8 bytes, to allow doubles }
  74. _dummy : ptrint;
  75. {$endif cpusparc}
  76. size : ptrint;
  77. next_fixed,
  78. prev_fixed : pmemchunk_fixed;
  79. end;
  80. pmemchunk_var = ^tmemchunk_var;
  81. tmemchunk_var = record
  82. prevsize : ptrint;
  83. size : ptrint;
  84. next_var,
  85. prev_var : pmemchunk_var;
  86. end;
  87. { ``header'', ie. size of structure valid when chunk is in use }
  88. { should correspond to tmemchunk_var_hdr structure starting with the
  89. last field. Reason is that the overlap is starting from the end of the
  90. record. }
  91. tmemchunk_fixed_hdr = record
  92. {$ifdef cpusparc}
  93. { Sparc needs to alloc aligned on 8 bytes, to allow doubles }
  94. _dummy : ptrint;
  95. {$endif cpusparc}
  96. size : ptrint;
  97. end;
  98. tmemchunk_var_hdr = record
  99. prevsize : ptrint;
  100. size : ptrint;
  101. end;
  102. poschunk = ^toschunk;
  103. toschunk = record
  104. size : ptrint;
  105. next,
  106. prev : poschunk;
  107. used : ptrint;
  108. end;
  109. tfreelists = array[1..maxblockindex] of pmemchunk_fixed;
  110. pfreelists = ^tfreelists;
  111. var
  112. {$ifdef HASGETFPCHEAPSTATUS}
  113. internal_status : TFPCHeapStatus;
  114. {$else HASGETFPCHEAPSTATUS}
  115. internal_status : THeapStatus;
  116. {$endif HASGETFPCHEAPSTATUS}
  117. freelists_fixed : tfreelists;
  118. freelist_var : pmemchunk_var;
  119. freeoslist : poschunk;
  120. freeoslistcount : dword;
  121. {$ifdef TestFreeLists}
  122. { this can be turned on by debugger }
  123. const
  124. test_each : boolean = false;
  125. {$endif TestFreeLists}
  126. {*****************************************************************************
  127. Memory Manager
  128. *****************************************************************************}
  129. procedure SetMemoryMutexManager(var MutexMgr: TMemoryMutexManager);
  130. begin
  131. { Release old mutexmanager, the default manager does nothing so
  132. calling this without initializing is safe }
  133. MemoryMutexManager.MutexDone;
  134. { Copy new mutexmanager }
  135. MemoryMutexManager := MutexMgr;
  136. { Init new mutexmanager }
  137. MemoryMutexManager.MutexInit;
  138. end;
  139. procedure GetMemoryManager(var MemMgr:TMemoryManager);
  140. begin
  141. if IsMultiThread and MemoryManager.NeedLock then
  142. begin
  143. try
  144. MemoryMutexManager.MutexLock;
  145. MemMgr := MemoryManager;
  146. finally
  147. MemoryMutexManager.MutexUnlock;
  148. end;
  149. end
  150. else
  151. begin
  152. MemMgr := MemoryManager;
  153. end;
  154. end;
  155. procedure SetMemoryManager(const MemMgr:TMemoryManager);
  156. begin
  157. if IsMultiThread and MemoryManager.NeedLock then
  158. begin
  159. try
  160. MemoryMutexManager.MutexLock;
  161. MemoryManager := MemMgr;
  162. finally
  163. MemoryMutexManager.MutexUnlock;
  164. end;
  165. end
  166. else
  167. begin
  168. MemoryManager := MemMgr;
  169. end;
  170. end;
  171. function IsMemoryManagerSet:Boolean;
  172. begin
  173. if IsMultiThread and MemoryManager.NeedLock then
  174. begin
  175. try
  176. MemoryMutexManager.MutexLock;
  177. IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or
  178. (MemoryManager.FreeMem<>@SysFreeMem);
  179. finally
  180. MemoryMutexManager.MutexUnlock;
  181. end;
  182. end
  183. else
  184. begin
  185. IsMemoryManagerSet := (MemoryManager.GetMem<>@SysGetMem) or
  186. (MemoryManager.FreeMem<>@SysFreeMem);
  187. end;
  188. end;
  189. procedure GetMem(Var p:pointer;Size:ptrint);
  190. begin
  191. if IsMultiThread and MemoryManager.NeedLock then
  192. begin
  193. try
  194. MemoryMutexManager.MutexLock;
  195. p := MemoryManager.GetMem(Size);
  196. finally
  197. MemoryMutexManager.MutexUnlock;
  198. end;
  199. end
  200. else
  201. begin
  202. p := MemoryManager.GetMem(Size);
  203. end;
  204. end;
  205. procedure GetMemory(Var p:pointer;Size:ptrint);
  206. begin
  207. GetMem(p,size);
  208. end;
  209. procedure FreeMem(p:pointer;Size:ptrint);
  210. begin
  211. if IsMultiThread and MemoryManager.NeedLock then
  212. begin
  213. try
  214. MemoryMutexManager.MutexLock;
  215. MemoryManager.FreeMemSize(p,Size);
  216. finally
  217. MemoryMutexManager.MutexUnlock;
  218. end;
  219. end
  220. else
  221. begin
  222. MemoryManager.FreeMemSize(p,Size);
  223. end;
  224. end;
  225. procedure FreeMemory(p:pointer;Size:ptrint);
  226. begin
  227. FreeMem(p,size);
  228. end;
  229. {$ifdef HASGETFPCHEAPSTATUS}
  230. function GetHeapStatus:THeapStatus;
  231. begin
  232. if IsMultiThread and MemoryManager.NeedLock then
  233. begin
  234. try
  235. MemoryMutexManager.MutexLock;
  236. result:=MemoryManager.GetHeapStatus();
  237. finally
  238. MemoryMutexManager.MutexUnlock;
  239. end;
  240. end
  241. else
  242. begin
  243. result:=MemoryManager.GetHeapStatus();
  244. end;
  245. end;
  246. function GetFPCHeapStatus:TFPCHeapStatus;
  247. begin
  248. if IsMultiThread and MemoryManager.NeedLock then
  249. begin
  250. try
  251. MemoryMutexManager.MutexLock;
  252. result:=MemoryManager.GetFPCHeapStatus();
  253. finally
  254. MemoryMutexManager.MutexUnlock;
  255. end;
  256. end
  257. else
  258. begin
  259. Result:=MemoryManager.GetFPCHeapStatus();
  260. end;
  261. end;
  262. {$else HASGETFPCHEAPSTATUS}
  263. procedure GetHeapStatus(var status:THeapStatus);
  264. begin
  265. if IsMultiThread and MemoryManager.NeedLock then
  266. begin
  267. try
  268. MemoryMutexManager.MutexLock;
  269. MemoryManager.GetHeapStatus(status);
  270. finally
  271. MemoryMutexManager.MutexUnlock;
  272. end;
  273. end
  274. else
  275. begin
  276. MemoryManager.GetHeapStatus(status);
  277. end;
  278. end;
  279. {$endif HASGETFPCHEAPSTATUS}
  280. function MemSize(p:pointer):ptrint;
  281. begin
  282. if IsMultiThread and MemoryManager.NeedLock then
  283. begin
  284. try
  285. MemoryMutexManager.MutexLock;
  286. MemSize := MemoryManager.MemSize(p);
  287. finally
  288. MemoryMutexManager.MutexUnlock;
  289. end;
  290. end
  291. else
  292. begin
  293. MemSize := MemoryManager.MemSize(p);
  294. end;
  295. end;
  296. { Delphi style }
  297. function FreeMem(p:pointer):ptrint;
  298. begin
  299. if IsMultiThread and MemoryManager.NeedLock then
  300. begin
  301. try
  302. MemoryMutexManager.MutexLock;
  303. Freemem := MemoryManager.FreeMem(p);
  304. finally
  305. MemoryMutexManager.MutexUnlock;
  306. end;
  307. end
  308. else
  309. begin
  310. Freemem := MemoryManager.FreeMem(p);
  311. end;
  312. end;
  313. function FreeMemory(p:pointer):ptrint;
  314. begin
  315. FreeMemory := FreeMem(p);
  316. end;
  317. function GetMem(size:ptrint):pointer;
  318. begin
  319. if IsMultiThread and MemoryManager.NeedLock then
  320. begin
  321. try
  322. MemoryMutexManager.MutexLock;
  323. GetMem := MemoryManager.GetMem(Size);
  324. finally
  325. MemoryMutexManager.MutexUnlock;
  326. end;
  327. end
  328. else
  329. begin
  330. GetMem := MemoryManager.GetMem(Size);
  331. end;
  332. end;
  333. function GetMemory(size:ptrint):pointer;
  334. begin
  335. GetMemory := Getmem(size);
  336. end;
  337. function AllocMem(Size:ptrint):pointer;
  338. begin
  339. if IsMultiThread and MemoryManager.NeedLock then
  340. begin
  341. try
  342. MemoryMutexManager.MutexLock;
  343. AllocMem := MemoryManager.AllocMem(size);
  344. finally
  345. MemoryMutexManager.MutexUnlock;
  346. end;
  347. end
  348. else
  349. begin
  350. AllocMem := MemoryManager.AllocMem(size);
  351. end;
  352. end;
  353. function ReAllocMem(var p:pointer;Size:ptrint):pointer;
  354. begin
  355. if IsMultiThread and MemoryManager.NeedLock then
  356. begin
  357. try
  358. MemoryMutexManager.MutexLock;
  359. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  360. finally
  361. MemoryMutexManager.MutexUnlock;
  362. end;
  363. end
  364. else
  365. begin
  366. ReAllocMem := MemoryManager.ReAllocMem(p,size);
  367. end;
  368. end;
  369. function ReAllocMemory(var p:pointer;Size:ptrint):pointer;
  370. begin
  371. ReAllocMemory := ReAllocMem(p,size);
  372. end;
  373. {$ifdef ValueGetmem}
  374. { Needed for calls from Assembler }
  375. function fpc_getmem(size:ptrint):pointer;compilerproc;[public,alias:'FPC_GETMEM'];
  376. begin
  377. if IsMultiThread and MemoryManager.NeedLock then
  378. begin
  379. try
  380. MemoryMutexManager.MutexLock;
  381. fpc_GetMem := MemoryManager.GetMem(size);
  382. finally
  383. MemoryMutexManager.MutexUnlock;
  384. end;
  385. end
  386. else
  387. begin
  388. fpc_GetMem := MemoryManager.GetMem(size);
  389. end;
  390. end;
  391. {$else ValueGetmem}
  392. { Needed for calls from Assembler }
  393. procedure AsmGetMem(var p:pointer;size:ptrint);[public,alias:'FPC_GETMEM'];
  394. begin
  395. p := MemoryManager.GetMem(size);
  396. end;
  397. {$endif ValueGetmem}
  398. {$ifdef ValueFreemem}
  399. procedure fpc_freemem(p:pointer);compilerproc;[public,alias:'FPC_FREEMEM'];
  400. begin
  401. if IsMultiThread and MemoryManager.NeedLock then
  402. begin
  403. try
  404. MemoryMutexManager.MutexLock;
  405. if p <> nil then
  406. MemoryManager.FreeMem(p);
  407. finally
  408. MemoryMutexManager.MutexUnlock;
  409. end;
  410. end
  411. else
  412. begin
  413. if p <> nil then
  414. MemoryManager.FreeMem(p);
  415. end;
  416. end;
  417. {$else ValueFreemem}
  418. procedure AsmFreeMem(var p:pointer);[public,alias:'FPC_FREEMEM'];
  419. begin
  420. if p <> nil then
  421. MemoryManager.FreeMem(p);
  422. end;
  423. {$endif ValueFreemem}
  424. { Bootstrapping }
  425. {$ifndef HASGETHEAPSTATUS}
  426. Function Memavail:ptrint;
  427. begin
  428. result:=0;
  429. end;
  430. Function Maxavail:ptrint;
  431. begin
  432. result:=0;
  433. end;
  434. Function Heapsize:ptrint;
  435. begin
  436. result:=0;
  437. end;
  438. {$endif HASGETHEAPSTATUS}
  439. {*****************************************************************************
  440. GetHeapStatus
  441. *****************************************************************************}
  442. {$ifdef HASGETFPCHEAPSTATUS}
  443. function SysGetFPCHeapStatus:TFPCHeapStatus;
  444. begin
  445. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  446. result:=internal_status;
  447. end;
  448. function SysGetHeapStatus :THeapStatus;
  449. begin
  450. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  451. result.TotalAllocated :=internal_status.CurrHeapUsed;
  452. result.TotalFree :=internal_status.CurrHeapFree;
  453. result.TotalAddrSpace :=0;
  454. result.TotalUncommitted :=0;
  455. result.TotalCommitted :=0;
  456. result.FreeSmall :=0;
  457. result.FreeBig :=0;
  458. result.Unused :=0;
  459. result.Overhead :=0;
  460. result.HeapErrorCode :=0;
  461. end;
  462. {$else}
  463. procedure SysGetHeapStatus(var status:THeapStatus);
  464. begin
  465. internal_status.CurrHeapFree:=internal_status.CurrHeapSize-internal_status.CurrHeapUsed;
  466. status:=internal_status;
  467. end;
  468. {$endif HASGETFPCHEAPSTATUS}
  469. {$ifdef DUMPBLOCKS} // TODO
  470. procedure DumpBlocks;
  471. var
  472. s,i,j : ptrint;
  473. hp : pfreerecord;
  474. begin
  475. for i := 1 to maxblock do
  476. begin
  477. hp := freelists[i];
  478. j := 0;
  479. while assigned(hp) do
  480. begin
  481. inc(j);
  482. hp := hp^.next;
  483. end;
  484. writeln('Block ',i*blocksize,': ',j);
  485. end;
  486. { freelist 0 }
  487. hp := freelists[0];
  488. j := 0;
  489. s := 0;
  490. while assigned(hp) do
  491. begin
  492. inc(j);
  493. if hp^.size>s then
  494. s := hp^.size;
  495. hp := hp^.next;
  496. end;
  497. writeln('Main: ',j,' maxsize: ',s);
  498. end;
  499. {$endif}
  500. {$ifdef TestFreeLists}
  501. procedure TestFreeLists;
  502. var
  503. i,j : ptrint;
  504. mc : pmemchunk_fixed;
  505. begin
  506. for i := 1 to maxblockindex do
  507. begin
  508. j := 0;
  509. mc := freelists_fixed[i];
  510. while assigned(mc) do
  511. begin
  512. inc(j);
  513. if ((mc^.size and fixedsizemask) <> i * blocksize) then
  514. RunError(204);
  515. mc := mc^.next_fixed;
  516. end;
  517. end;
  518. end;
  519. {$endif TestFreeLists}
  520. {*****************************************************************************
  521. List adding/removal
  522. *****************************************************************************}
  523. procedure append_to_list_fixed(blockindex: ptrint; pmc: pmemchunk_fixed);
  524. begin
  525. pmc^.prev_fixed := nil;
  526. pmc^.next_fixed := freelists_fixed[blockindex];
  527. if freelists_fixed[blockindex]<>nil then
  528. freelists_fixed[blockindex]^.prev_fixed := pmc;
  529. freelists_fixed[blockindex] := pmc;
  530. end;
  531. procedure append_to_list_var(pmc: pmemchunk_var);
  532. begin
  533. pmc^.prev_var := nil;
  534. pmc^.next_var := freelist_var;
  535. if freelist_var<>nil then
  536. freelist_var^.prev_var := pmc;
  537. freelist_var := pmc;
  538. end;
  539. procedure remove_from_list_fixed(blockindex: ptrint; pmc: pmemchunk_fixed);
  540. begin
  541. if assigned(pmc^.next_fixed) then
  542. pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed;
  543. if assigned(pmc^.prev_fixed) then
  544. pmc^.prev_fixed^.next_fixed := pmc^.next_fixed
  545. else
  546. freelists_fixed[blockindex] := pmc^.next_fixed;
  547. end;
  548. procedure remove_from_list_var(pmc: pmemchunk_var);
  549. begin
  550. if assigned(pmc^.next_var) then
  551. pmc^.next_var^.prev_var := pmc^.prev_var;
  552. if assigned(pmc^.prev_var) then
  553. pmc^.prev_var^.next_var := pmc^.next_var
  554. else
  555. freelist_var := pmc^.next_var;
  556. end;
  557. procedure append_to_oslist(poc: poschunk);
  558. begin
  559. { decide whether to free block or add to list }
  560. {$ifdef HAS_SYSOSFREE}
  561. if freeoslistcount >= 3 then
  562. begin
  563. dec(internal_status.currheapsize, poc^.size);
  564. SysOSFree(poc, poc^.size);
  565. end
  566. else
  567. begin
  568. {$endif}
  569. poc^.prev := nil;
  570. poc^.next := freeoslist;
  571. if freeoslist <> nil then
  572. freeoslist^.prev := poc;
  573. freeoslist := poc;
  574. inc(freeoslistcount);
  575. {$ifdef HAS_SYSOSFREE}
  576. end;
  577. {$endif}
  578. end;
  579. procedure remove_from_oslist(poc: poschunk);
  580. begin
  581. if assigned(poc^.next) then
  582. poc^.next^.prev := poc^.prev;
  583. if assigned(poc^.prev) then
  584. poc^.prev^.next := poc^.next
  585. else
  586. freeoslist := poc^.next;
  587. dec(freeoslistcount);
  588. end;
  589. procedure append_to_oslist_var(pmc: pmemchunk_var);
  590. var
  591. poc: poschunk;
  592. begin
  593. // block eligable for freeing
  594. poc := pointer(pmc)-sizeof(toschunk);
  595. remove_from_list_var(pmc);
  596. append_to_oslist(poc);
  597. end;
  598. procedure append_to_oslist_fixed(blockindex, chunksize: ptrint; poc: poschunk);
  599. var
  600. pmc: pmemchunk_fixed;
  601. i, count: ptrint;
  602. begin
  603. count := (poc^.size - sizeof(toschunk)) div chunksize;
  604. pmc := pmemchunk_fixed(pointer(poc)+sizeof(toschunk));
  605. for i := 0 to count - 1 do
  606. begin
  607. remove_from_list_fixed(blockindex, pmc);
  608. pmc := pointer(pmc)+chunksize;
  609. end;
  610. append_to_oslist(poc);
  611. end;
  612. {*****************************************************************************
  613. Split block
  614. *****************************************************************************}
  615. procedure split_block(pcurr: pmemchunk_var; size: ptrint);
  616. var
  617. pcurr_tmp : pmemchunk_var;
  618. sizeleft: ptrint;
  619. begin
  620. sizeleft := (pcurr^.size and sizemask)-size;
  621. if sizeleft>=blocksize then
  622. begin
  623. pcurr_tmp := pmemchunk_var(pointer(pcurr)+size);
  624. { update prevsize of block to the right }
  625. if (pcurr^.size and lastblockflag) = 0 then
  626. pmemchunk_var(pointer(pcurr)+(pcurr^.size and sizemask))^.prevsize := sizeleft;
  627. { inherit the lastblockflag }
  628. pcurr_tmp^.size := sizeleft or (pcurr^.size and lastblockflag);
  629. pcurr_tmp^.prevsize := size;
  630. { the block we return is not the last one anymore (there's now a block after it) }
  631. { decrease size of block to new size }
  632. pcurr^.size := size or (pcurr^.size and (not sizemask and not lastblockflag));
  633. { insert the block in the freelist }
  634. append_to_list_var(pcurr_tmp);
  635. end;
  636. end;
  637. {*****************************************************************************
  638. Try concat freerecords
  639. *****************************************************************************}
  640. procedure concat_two_blocks(mc_left, mc_right: pmemchunk_var);
  641. var
  642. mc_tmp : pmemchunk_var;
  643. size_right : ptrint;
  644. begin
  645. // mc_right can't be a fixed size block
  646. if mc_right^.size and fixedsizeflag<>0 then
  647. HandleError(204);
  648. // left block free, concat with right-block
  649. size_right := mc_right^.size and sizemask;
  650. inc(mc_left^.size, size_right);
  651. // if right-block was last block, copy flag
  652. if (mc_right^.size and lastblockflag) <> 0 then
  653. begin
  654. mc_left^.size := mc_left^.size or lastblockflag;
  655. end
  656. else
  657. begin
  658. // there is a block to the right of the right-block, adjust it's prevsize
  659. mc_tmp := pmemchunk_var(pointer(mc_right)+size_right);
  660. mc_tmp^.prevsize := mc_left^.size and sizemask;
  661. end;
  662. // remove right-block from doubly linked list
  663. remove_from_list_var(mc_right);
  664. end;
  665. procedure try_concat_free_chunk_forward(mc: pmemchunk_var);
  666. var
  667. mc_tmp : pmemchunk_var;
  668. begin
  669. { try concat forward }
  670. if (mc^.size and lastblockflag) = 0 then
  671. begin
  672. mc_tmp := pmemchunk_var(pointer(mc)+(mc^.size and sizemask));
  673. if (mc_tmp^.size and usedflag) = 0 then
  674. begin
  675. // next block free: concat
  676. concat_two_blocks(mc, mc_tmp);
  677. end;
  678. end;
  679. end;
  680. function try_concat_free_chunk(mc: pmemchunk_var): pmemchunk_var;
  681. var
  682. mc_tmp : pmemchunk_var;
  683. begin
  684. try_concat_free_chunk_forward(mc);
  685. { try concat backward }
  686. if (mc^.size and firstblockflag) = 0 then
  687. begin
  688. mc_tmp := pmemchunk_var(pointer(mc)-mc^.prevsize);
  689. if (mc_tmp^.size and usedflag) = 0 then
  690. begin
  691. // prior block free: concat
  692. concat_two_blocks(mc_tmp, mc);
  693. mc := mc_tmp;
  694. end;
  695. end;
  696. result := mc;
  697. end;
  698. {*****************************************************************************
  699. Grow Heap
  700. *****************************************************************************}
  701. function alloc_oschunk(blockindex, size: ptrint): pointer;
  702. var
  703. pmc : pmemchunk_fixed;
  704. pmcv : pmemchunk_var;
  705. minsize,
  706. maxsize,
  707. i, count : ptrint;
  708. chunksize : ptrint;
  709. begin
  710. { increase size by size needed for os block header }
  711. minsize := size + sizeof(toschunk);
  712. if blockindex<>0 then
  713. maxsize := (size * $ffff) + sizeof(toschunk)
  714. else
  715. maxsize := high(ptrint);
  716. { blocks available in freelist? }
  717. result := freeoslist;
  718. while result <> nil do
  719. begin
  720. if (poschunk(result)^.size >= minsize) and
  721. (poschunk(result)^.size <= maxsize) then
  722. begin
  723. size := poschunk(result)^.size;
  724. remove_from_oslist(poschunk(result));
  725. break;
  726. end;
  727. result := poschunk(result)^.next;
  728. end;
  729. if result = nil then
  730. begin
  731. {$ifdef DUMPGROW}
  732. writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and $ffff0000);
  733. DumpBlocks;
  734. {$endif}
  735. { allocate by 64K size }
  736. size := (size+sizeof(toschunk)+$ffff) and not $ffff;
  737. { allocate smaller blocks for fixed-size chunks }
  738. if blockindex<>0 then
  739. begin
  740. result := SysOSAlloc(GrowHeapSizeSmall);
  741. if result<>nil then
  742. size := GrowHeapSizeSmall;
  743. end
  744. { first try 256K (default) }
  745. else if size<=GrowHeapSize1 then
  746. begin
  747. result := SysOSAlloc(GrowHeapSize1);
  748. if result<>nil then
  749. size := GrowHeapSize1;
  750. end
  751. { second try 1024K (default) }
  752. else if size<=GrowHeapSize2 then
  753. begin
  754. result := SysOSAlloc(GrowHeapSize2);
  755. if result<>nil then
  756. size := GrowHeapSize2;
  757. end
  758. { else allocate the needed bytes }
  759. else
  760. result := SysOSAlloc(size);
  761. { try again }
  762. if result=nil then
  763. begin
  764. result := SysOSAlloc(size);
  765. if (result=nil) then
  766. begin
  767. if ReturnNilIfGrowHeapFails then
  768. exit
  769. else
  770. HandleError(203);
  771. end;
  772. end;
  773. { set the total new heap size }
  774. inc(internal_status.currheapsize,size);
  775. if internal_status.currheapsize>internal_status.maxheapsize then
  776. internal_status.maxheapsize:=internal_status.currheapsize;
  777. end;
  778. { initialize os-block }
  779. poschunk(result)^.used := 0;
  780. poschunk(result)^.size := size;
  781. inc(result, sizeof(toschunk));
  782. if blockindex<>0 then
  783. begin
  784. { chop os chunk in fixedsize parts,
  785. maximum of $ffff elements are allowed, otherwise
  786. there will be an overflow }
  787. chunksize := blockindex shl blockshr;
  788. count := (size-sizeof(toschunk)) div chunksize;
  789. if count>$ffff then
  790. HandleError(204);
  791. pmc := pmemchunk_fixed(result);
  792. pmc^.prev_fixed := nil;
  793. i := 0;
  794. repeat
  795. pmc^.size := fixedsizeflag or chunksize or (i shl 16);
  796. pmc^.next_fixed := pointer(pmc)+chunksize;
  797. inc(i);
  798. if i < count then
  799. begin
  800. pmc := pmemchunk_fixed(pointer(pmc)+chunksize);
  801. pmc^.prev_fixed := pointer(pmc)-chunksize;
  802. end
  803. else
  804. begin
  805. break;
  806. end;
  807. until false;
  808. append_to_list_fixed(blockindex, pmc);
  809. pmc^.prev_fixed := pointer(pmc)-chunksize;
  810. freelists_fixed[blockindex] := pmemchunk_fixed(result);
  811. end
  812. else
  813. begin
  814. pmcv := pmemchunk_var(result);
  815. append_to_list_var(pmcv);
  816. pmcv^.size := ((size-sizeof(toschunk)) and sizemask) or (firstblockflag or lastblockflag);
  817. pmcv^.prevsize := 0;
  818. end;
  819. {$ifdef TestFreeLists}
  820. TestFreeLists;
  821. {$endif TestFreeLists}
  822. end;
  823. {*****************************************************************************
  824. SysGetMem
  825. *****************************************************************************}
  826. function SysGetMem_Fixed(size: ptrint): pointer;
  827. var
  828. pcurr: pmemchunk_fixed;
  829. poc: poschunk;
  830. s: ptrint;
  831. begin
  832. result:=nil;
  833. { try to find a block in one of the freelists per size }
  834. s := size shr blockshr;
  835. pcurr := freelists_fixed[s];
  836. { no free blocks ? }
  837. if not assigned(pcurr) then
  838. begin
  839. pcurr := alloc_oschunk(s, size);
  840. if not assigned(pcurr) then
  841. exit;
  842. end;
  843. { get a pointer to the block we should return }
  844. result := pointer(pcurr)+sizeof(tmemchunk_fixed_hdr);
  845. { flag as in-use }
  846. pcurr^.size := pcurr^.size or usedflag;
  847. { update freelist }
  848. freelists_fixed[s] := pcurr^.next_fixed;
  849. if assigned(freelists_fixed[s]) then
  850. freelists_fixed[s]^.prev_fixed := nil;
  851. poc := poschunk(pointer(pcurr)-((pcurr^.size shr 16)*(pcurr^.size and fixedsizemask)+sizeof(toschunk)));
  852. inc(poc^.used);
  853. { statistics }
  854. inc(internal_status.currheapused,size);
  855. if internal_status.currheapused>internal_status.maxheapused then
  856. internal_status.maxheapused:=internal_status.currheapused;
  857. {$ifdef TestFreeLists}
  858. if test_each then
  859. TestFreeLists;
  860. {$endif TestFreeLists}
  861. end;
  862. function SysGetMem_Var(size: ptrint): pointer;
  863. var
  864. pcurr, pcurr_tmp : pmemchunk_var;
  865. {$ifdef BESTMATCH}
  866. pbest : pmemchunk_var;
  867. {$endif}
  868. begin
  869. result:=nil;
  870. {$ifdef BESTMATCH}
  871. pbest := nil;
  872. {$endif}
  873. pcurr := freelist_var;
  874. while assigned(pcurr) do
  875. begin
  876. {$ifdef BESTMATCH}
  877. if pcurr^.size=size then
  878. begin
  879. break;
  880. end
  881. else
  882. begin
  883. if (pcurr^.size>size) then
  884. begin
  885. if (not assigned(pbest)) or
  886. (pcurr^.size<pbest^.size) then
  887. pbest := pcurr;
  888. end;
  889. end;
  890. {$else BESTMATCH}
  891. if pcurr^.size>=size then
  892. break;
  893. {$endif BESTMATCH}
  894. pcurr := pcurr^.next_var;
  895. end;
  896. {$ifdef BESTMATCH}
  897. if not assigned(pcurr) then
  898. pcurr := pbest;
  899. {$endif}
  900. if not assigned(pcurr) then
  901. begin
  902. // all os-chunks full, allocate a new one
  903. pcurr := alloc_oschunk(0, size);
  904. if not assigned(pcurr) then
  905. exit;
  906. end;
  907. { get pointer of the block we should return }
  908. result := pointer(pcurr)+sizeof(tmemchunk_var_hdr);
  909. { remove the current block from the freelist }
  910. remove_from_list_var(pcurr);
  911. { create the left over freelist block, if at least 16 bytes are free }
  912. split_block(pcurr, size);
  913. { flag block as used }
  914. pcurr^.size := pcurr^.size or usedflag;
  915. { statistics }
  916. inc(internal_status.currheapused,size);
  917. if internal_status.currheapused>internal_status.maxheapused then
  918. internal_status.maxheapused:=internal_status.currheapused;
  919. {$ifdef TestFreeLists}
  920. if test_each then
  921. TestFreeLists;
  922. {$endif TestFreeLists}
  923. end;
  924. function SysGetMem(size : ptrint):pointer;
  925. begin
  926. { Something to allocate ? }
  927. if size<=0 then
  928. begin
  929. { give an error for < 0 }
  930. if size<0 then
  931. HandleError(204);
  932. { we always need to allocate something, using heapend is not possible,
  933. because heappend can be changed by growheap (PFV) }
  934. size := 1;
  935. end;
  936. { calc to multiple of 16 after adding the needed bytes for memchunk header }
  937. if size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr)) then
  938. begin
  939. size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask;
  940. sysgetmem := sysgetmem_fixed(size);
  941. end
  942. else
  943. begin
  944. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  945. sysgetmem := sysgetmem_var(size);
  946. end;
  947. end;
  948. {*****************************************************************************
  949. SysFreeMem
  950. *****************************************************************************}
  951. function SysFreeMem_Fixed(pcurr: pmemchunk_fixed; size: ptrint): ptrint;
  952. var
  953. pcurrsize: ptrint;
  954. blockindex: ptrint;
  955. poc: poschunk;
  956. begin
  957. pcurrsize := pcurr^.size and fixedsizemask;
  958. if size<>pcurrsize then
  959. HandleError(204);
  960. dec(internal_status.currheapused,pcurrsize);
  961. { insert the block in it's freelist }
  962. pcurr^.size := pcurr^.size and (not usedflag);
  963. blockindex := pcurrsize shr blockshr;
  964. append_to_list_fixed(blockindex, pcurr);
  965. { decrease used blocks count }
  966. poc := poschunk(pointer(pcurr)-(pcurr^.size shr 16)*pcurrsize-sizeof(toschunk));
  967. if poc^.used = 0 then
  968. HandleError(204);
  969. dec(poc^.used);
  970. if poc^.used = 0 then
  971. begin
  972. // block eligable for freeing
  973. append_to_oslist_fixed(blockindex, pcurrsize, poc);
  974. end;
  975. SysFreeMem_Fixed := pcurrsize;
  976. {$ifdef TestFreeLists}
  977. if test_each then
  978. TestFreeLists;
  979. {$endif TestFreeLists}
  980. end;
  981. function SysFreeMem_Var(pcurr: pmemchunk_var; size: ptrint): ptrint;
  982. var
  983. pcurrsize: ptrint;
  984. begin
  985. pcurrsize := pcurr^.size and sizemask;
  986. if size<>pcurrsize then
  987. HandleError(204);
  988. dec(internal_status.currheapused,pcurrsize);
  989. { insert the block in it's freelist }
  990. pcurr^.size := pcurr^.size and (not usedflag);
  991. append_to_list_var(pcurr);
  992. SysFreeMem_Var := pcurrsize;
  993. pcurr := try_concat_free_chunk(pcurr);
  994. if (pcurr^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then
  995. begin
  996. append_to_oslist_var(pcurr);
  997. end;
  998. {$ifdef TestFreeLists}
  999. if test_each then
  1000. TestFreeLists;
  1001. {$endif TestFreeLists}
  1002. end;
  1003. function SysFreeMem(p: pointer): ptrint;
  1004. var
  1005. pcurrsize: ptrint;
  1006. begin
  1007. if p=nil then
  1008. exit;
  1009. pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1010. { check if this is a fixed- or var-sized chunk }
  1011. if (pcurrsize and fixedsizeflag) = 0 then
  1012. begin
  1013. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)), pcurrsize and sizemask);
  1014. end
  1015. else
  1016. begin
  1017. result := sysfreemem_fixed(pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr)), pcurrsize and fixedsizemask);
  1018. end;
  1019. end;
  1020. {*****************************************************************************
  1021. SysFreeMemSize
  1022. *****************************************************************************}
  1023. Function SysFreeMemSize(p: pointer; size: ptrint):ptrint;
  1024. var
  1025. pcurrsize: ptrint;
  1026. begin
  1027. SysFreeMemSize := 0;
  1028. if size<=0 then
  1029. begin
  1030. if size<0 then
  1031. HandleError(204);
  1032. exit;
  1033. end;
  1034. if p=nil then
  1035. HandleError(204);
  1036. pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1037. { check if this is a fixed- or var-sized chunk }
  1038. if (pcurrsize and fixedsizeflag) = 0 then
  1039. begin
  1040. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  1041. result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)), size);
  1042. end
  1043. else
  1044. begin
  1045. size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask;
  1046. result := sysfreemem_fixed(pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr)), size);
  1047. end;
  1048. end;
  1049. {*****************************************************************************
  1050. SysMemSize
  1051. *****************************************************************************}
  1052. function SysMemSize(p: pointer): ptrint;
  1053. begin
  1054. SysMemSize := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size;
  1055. if (SysMemSize and fixedsizeflag) = 0 then
  1056. begin
  1057. SysMemSize := SysMemSize and sizemask;
  1058. dec(SysMemSize, sizeof(tmemchunk_var_hdr));
  1059. end
  1060. else
  1061. begin
  1062. SysMemSize := SysMemSize and fixedsizemask;
  1063. dec(SysMemSize, sizeof(tmemchunk_fixed_hdr));
  1064. end;
  1065. end;
  1066. {*****************************************************************************
  1067. SysAllocMem
  1068. *****************************************************************************}
  1069. function SysAllocMem(size: ptrint): pointer;
  1070. begin
  1071. sysallocmem := MemoryManager.GetMem(size);
  1072. if sysallocmem<>nil then
  1073. FillChar(sysallocmem^,MemoryManager.MemSize(sysallocmem),0);
  1074. end;
  1075. {*****************************************************************************
  1076. SysResizeMem
  1077. *****************************************************************************}
  1078. function SysTryResizeMem(var p: pointer; size: ptrint): boolean;
  1079. var
  1080. pcurrsize,
  1081. oldsize,
  1082. currsize,
  1083. sizeleft : ptrint;
  1084. pnew,
  1085. pcurr : pmemchunk_var;
  1086. begin
  1087. { fix p to point to the heaprecord }
  1088. pcurrsize := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr))^.size;
  1089. if (pcurrsize and fixedsizeflag) = 0 then
  1090. begin
  1091. currsize := pcurrsize and sizemask;
  1092. size := (size+sizeof(tmemchunk_var_hdr)+(blocksize-1)) and sizemask;
  1093. end
  1094. else
  1095. begin
  1096. currsize := pcurrsize and fixedsizemask;
  1097. size := (size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and fixedsizemask;
  1098. end;
  1099. { is the allocated block still correct? }
  1100. if (currsize>=size) and (size>(currsize-blocksize)) then
  1101. begin
  1102. SysTryResizeMem := true;
  1103. {$ifdef TestFreeLists}
  1104. if test_each then
  1105. TestFreeLists;
  1106. {$endif TestFreeLists}
  1107. exit;
  1108. end;
  1109. { don't do resizes on fixed-size blocks }
  1110. if (pcurrsize and fixedsizeflag) <> 0 then
  1111. begin
  1112. SysTryResizeMem := false;
  1113. exit;
  1114. end;
  1115. { get pointer to block }
  1116. pcurr := pmemchunk_var(pointer(p)-sizeof(tmemchunk_var_hdr));
  1117. oldsize := currsize;
  1118. { do we need to allocate more memory ? }
  1119. if size>currsize then
  1120. begin
  1121. { the size is bigger than the previous size, we need to allocated more mem.
  1122. We first check if the blocks after the current block are free. If not we
  1123. simply call getmem/freemem to get the new block }
  1124. try_concat_free_chunk_forward(pcurr);
  1125. currsize := (pcurr^.size and sizemask);
  1126. SysTryResizeMem := currsize>=size;
  1127. end;
  1128. if currsize>size then
  1129. begin
  1130. { is the size smaller then we can adjust the block to that size and insert
  1131. the other part into the freelist }
  1132. { create the left over freelist block, if at least 16 bytes are free }
  1133. split_block(pcurr, size);
  1134. SysTryResizeMem := true;
  1135. end;
  1136. inc(internal_status.currheapused,size-oldsize);
  1137. {$ifdef TestFreeLists}
  1138. if test_each then
  1139. TestFreeLists;
  1140. {$endif TestFreeLists}
  1141. end;
  1142. {*****************************************************************************
  1143. SysResizeMem
  1144. *****************************************************************************}
  1145. function SysReAllocMem(var p: pointer; size: ptrint):pointer;
  1146. var
  1147. minsize : ptrint;
  1148. p2 : pointer;
  1149. begin
  1150. { Free block? }
  1151. if size=0 then
  1152. begin
  1153. if p<>nil then
  1154. begin
  1155. MemoryManager.FreeMem(p);
  1156. p := nil;
  1157. end;
  1158. end
  1159. else
  1160. { Allocate a new block? }
  1161. if p=nil then
  1162. begin
  1163. p := MemoryManager.GetMem(size);
  1164. end
  1165. else
  1166. { Resize block }
  1167. if not SysTryResizeMem(p,size) then
  1168. begin
  1169. minsize := MemoryManager.MemSize(p);
  1170. if size < minsize then
  1171. minsize := size;
  1172. p2 := MemoryManager.GetMem(size);
  1173. if p2<>nil then
  1174. Move(p^,p2^,minsize);
  1175. MemoryManager.FreeMem(p);
  1176. p := p2;
  1177. end;
  1178. SysReAllocMem := p;
  1179. end;
  1180. {*****************************************************************************
  1181. MemoryMutexManager default hooks
  1182. *****************************************************************************}
  1183. procedure SysHeapMutexInit;
  1184. begin
  1185. { nothing todo }
  1186. end;
  1187. procedure SysHeapMutexDone;
  1188. begin
  1189. { nothing todo }
  1190. end;
  1191. procedure SysHeapMutexLock;
  1192. begin
  1193. { give an runtime error. the program is running multithreaded without
  1194. any heap protection. this will result in unpredictable errors so
  1195. stopping here with an error is more safe (PFV) }
  1196. runerror(244);
  1197. end;
  1198. procedure SysHeapMutexUnLock;
  1199. begin
  1200. { see SysHeapMutexLock for comment }
  1201. runerror(244);
  1202. end;
  1203. {*****************************************************************************
  1204. InitHeap
  1205. *****************************************************************************}
  1206. { This function will initialize the Heap manager and need to be called from
  1207. the initialization of the system unit }
  1208. procedure InitHeap;
  1209. begin
  1210. FillChar(freelists_fixed,sizeof(tfreelists),0);
  1211. freelist_var := nil;
  1212. freeoslist := nil;
  1213. freeoslistcount := 0;
  1214. fillchar(internal_status,sizeof(internal_status),0);
  1215. end;
  1216. {
  1217. $Log$
  1218. Revision 1.49 2005-03-21 16:31:33 peter
  1219. * fix crash under win32 with previous reallocmem fix
  1220. Revision 1.48 2005/03/20 18:57:29 peter
  1221. * fixed tryresizemem
  1222. Revision 1.47 2005/03/04 16:49:34 peter
  1223. * fix getheapstatus bootstrapping
  1224. Revision 1.46 2005/03/02 14:25:19 marco
  1225. * small typo fix on last commit
  1226. Revision 1.45 2005/03/02 10:46:10 marco
  1227. * getfpcheapstatus now also on memmgr
  1228. Revision 1.44 2005/02/28 15:38:38 marco
  1229. * getFPCheapstatus (no, FPC HEAP, not FP CHEAP!)
  1230. Revision 1.43 2005/02/14 17:13:22 peter
  1231. * truncate log
  1232. Revision 1.42 2005/01/30 11:56:29 peter
  1233. * allow Freemem(nil)
  1234. }