heap.inc 34 KB

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