heap.inc 34 KB

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