heap.inc 34 KB

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