heap.inc 35 KB

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